Skip to contentSkip to conversation

ACPI/IPMI: Fix several issues in the current codes

PATCH Superseded Lv Zheng 5 participants 61 messages Jul 23, 2013 – Sep 27, 2013 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

Lv Zheng

author
#1 lore raw

cover ACPI/IPMI: Fix several issues in the current codes

This patchset tries to fix the following kernel bug:

This is fixed by [PATCH 05].

The bug shows IPMI operation region may appear in a device not under the IPMI system interface device's scope, thus it's required to install the ACPI IPMI operation region handler from the root of the ACPI namespace.

The original acpi_ipmi implementation includes several issues that break the test process. This patchset also includes a re-design of acpi_ipmi module to make the test possible.

[PATCH 01-05] are bug-fix patches that can be applied to the kernels whose
version is > 2.6.38. This can be confirmed with:
# git tag --contains e92b297c
[PATCH 06] is also a bug-fix patch.
The drivers/acpi/osl.c part can be back ported to the kernels whose version > 2.6.14. This can be confirmed with:
# git tag --contains 4be44fcd
The drivers/acpi/acpi_ipmi.c part can be applied on top of [PATCH 01-05].
[PATCH 07] is a tuning patch for acpi_ipmi.c.
[PATCH 08-10] are cleanup patches for acpi_ipmi.c.
[PATCH 11] is a cleanup patch not for acpi_ipmi.c.
[PATCH 12-13] are test patches.
[PATCH 12] may be accepted by upstream kernel as a useful
facility to test the loading/unloading of the modules.
[PATCH 13] should not be merged by any published kernel as it
is a driver for a pseudo device with a PnP ID that does not exist in the real machines.

This patchset has passed the test around a fake device accessing IPMI operation region fields on an IPMI capable platform. A stress test of module(acpi_ipmi) load/unload has been performed on such platform. No races can be found and the IPMI operation region handler is functioning now. It is not possible to test module(ipmi_si) load/unload as it can't be unloaded due to its' transfer flushing implementation.

Lv Zheng (13):
ACPI/IPMI: Fix potential response buffer overflow ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler() ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user ACPI/IPMI: Fix issue caused by the per-device registration of the
IPMI operation region handler
ACPI/IPMI: Add reference counting for ACPI operation region handlers ACPI/IPMI: Add reference counting for ACPI IPMI transfers ACPI/IPMI: Cleanup several acpi_ipmi_device members ACPI/IPMI: Cleanup some initialization codes
ACPI/IPMI: Cleanup some inclusion codes
ACPI/IPMI: Cleanup some Kconfig codes
Testing: Add module load/unload test suite
ACPI/IPMI: Add IPMI operation region test device driver

 drivers/acpi/Kconfig                          |   71 +++-
 drivers/acpi/Makefile                         |    1 +
 drivers/acpi/acpi_ipmi.c                      |  513 +++++++++++++++----------
 drivers/acpi/ipmi_test.c                      |  254 ++++++++++++
 drivers/acpi/osl.c                            |  224 +++++++++++
 include/acpi/acpi_bus.h                       |    5 +
 tools/testing/module-unloading/endless_cat.sh |   32 ++
 tools/testing/module-unloading/endless_mod.sh |   81 ++++
 8 files changed, 977 insertions(+), 204 deletions(-)
 create mode 100644 drivers/acpi/ipmi_test.c
 create mode 100755 tools/testing/module-unloading/endless_cat.sh
 create mode 100755 tools/testing/module-unloading/endless_mod.sh
Signature · 1 line
1.7.10
author
#2 lore raw

PATCH 1/13 ACPI/IPMI: Fix potential response buffer overflow

This patch enhances sanity checks on message size to avoid potential buffer overflow.

The kernel IPMI message size is IPMI_MAX_MSG_LENGTH(272 bytes) while the ACPI specification defined IPMI message size is 64 bytes. The difference is not handled by the original codes. This may cause crash in the response handling codes.
This patch fixes this gap and also combines rx_data/tx_data to use single data/len pair since they need not be seperated.

---
 drivers/acpi/acpi_ipmi.c |  100 ++++++++++++++++++++++++++++------------------
 1 file changed, 61 insertions(+), 39 deletions(-)
drivers/acpi/acpi_ipmi.c +61 -39
index f40acef..28e2b4c 100644
@@ -51,6 +51,7 @@ MODULE_LICENSE("GPL");
 #define ACPI_IPMI_UNKNOWN		0x07
 /* the IPMI timeout is 5s */
 #define IPMI_TIMEOUT			(5 * HZ)
+#define ACPI_IPMI_MAX_MSG_LENGTH	64
 
 struct acpi_ipmi_device {
 	/* the device list attached to driver_data.ipmi_devices */
@@ -89,11 +90,9 @@ struct acpi_ipmi_msg {
 	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;
+	/* tx/rx data . And copy it from/to ACPI object buffer */
+	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
+	u8	rx_len;
 	struct acpi_ipmi_device *device;
 };
 
@@ -101,7 +100,7 @@ struct acpi_ipmi_msg {
 struct acpi_ipmi_buffer {
 	u8 status;
 	u8 length;
-	u8 data[64];
+	u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
 };
 
 static void ipmi_register_bmc(int iface, struct device *dev);
@@ -140,9 +139,9 @@ static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 
 #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)
+static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
+				    acpi_physical_address address,
+				    acpi_integer *value)
 {
 	struct kernel_ipmi_msg *msg;
 	struct acpi_ipmi_buffer *buffer;
@@ -155,15 +154,21 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 	 */
 	msg->netfn = IPMI_OP_RGN_NETFN(address);
 	msg->cmd = IPMI_OP_RGN_CMD(address);
-	msg->data = tx_msg->tx_data;
+	msg->data = tx_msg->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 */
+	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
+		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+			      "Unexpected request (msg len %d).\n",
+			      buffer->length);
+		return -EINVAL;
+	}
 	msg->data_len = buffer->length;
-	memcpy(tx_msg->tx_data, buffer->data, msg->data_len);
+	memcpy(tx_msg->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,
@@ -181,10 +186,12 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 	device->curr_msgid++;
 	tx_msg->tx_msgid = device->curr_msgid;
 	mutex_unlock(&device->tx_msg_lock);
+
+	return 0;
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
-		acpi_integer *value, int rem_time)
+				      acpi_integer *value, int rem_time)
 {
 	struct acpi_ipmi_buffer *buffer;
 
@@ -206,13 +213,14 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 		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);
+	memcpy(buffer->data, msg->data, msg->rx_len);
 }
 
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
@@ -244,12 +252,12 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
-			"returned user %p, expected user %p\n",
-			msg->user, ipmi_device->user_interface);
-		ipmi_free_recv_msg(msg);
-		return;
+		dev_warn(&pnp_dev->dev,
+			 "Unexpected response is returned. returned user %p, expected user %p\n",
+			 msg->user, ipmi_device->user_interface);
+		goto out_msg;
 	}
+
 	mutex_lock(&ipmi_device->tx_msg_lock);
 	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
 		if (msg->msgid == tx_msg->tx_msgid) {
@@ -257,24 +265,31 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			break;
 		}
 	}
-
 	mutex_unlock(&ipmi_device->tx_msg_lock);
+
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
-			"returned.\n", msg->msgid);
-		ipmi_free_recv_msg(msg);
-		return;
+		dev_warn(&pnp_dev->dev,
+			 "Unexpected response (msg id %ld) is returned.\n",
+			 msg->msgid);
+		goto out_msg;
 	}
 
-	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;
+	/* copy the response data to Rx_data buffer */
+	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
+		dev_WARN_ONCE(&pnp_dev->dev, true,
+			      "Unexpected response (msg len %d).\n",
+			      msg->msg.data_len);
+		goto out_comp;
 	}
+	tx_msg->rx_len = msg->msg.data_len;
+	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	tx_msg->msg_done = 1;
+
+out_comp:
 	complete(&tx_msg->tx_complete);
+out_msg:
 	ipmi_free_recv_msg(msg);
-};
+}
 
 static void ipmi_register_bmc(int iface, struct device *dev)
 {
@@ -353,6 +368,7 @@ static void ipmi_bmc_gone(int iface)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 }
+
 /* --------------------------------------------------------------------------
  *			Address Space Management
  * -------------------------------------------------------------------------- */
@@ -371,13 +387,14 @@ static void ipmi_bmc_gone(int iface)
 
 static acpi_status
 acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
-		      u32 bits, acpi_integer *value,
-		      void *handler_context, void *region_context)
+			u32 bits, acpi_integer *value,
+			void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
 	struct acpi_ipmi_device *ipmi_device = handler_context;
 	int err, rem_time;
 	acpi_status status;
+
 	/*
 	 * IPMI opregion message.
 	 * IPMI message is firstly written to the BMC and system software
@@ -394,28 +411,33 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if (!tx_msg)
 		return AE_NO_MEMORY;
 
-	acpi_format_ipmi_msg(tx_msg, address, value);
+	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
+		status = AE_TYPE;
+		goto out_msg;
+	}
+
 	mutex_lock(&ipmi_device->tx_msg_lock);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	mutex_unlock(&ipmi_device->tx_msg_lock);
 	err = ipmi_request_settime(ipmi_device->user_interface,
-					&tx_msg->addr,
-					tx_msg->tx_msgid,
-					&tx_msg->tx_message,
-					NULL, 0, 0, 0);
+				   &tx_msg->addr,
+				   tx_msg->tx_msgid,
+				   &tx_msg->tx_message,
+				   NULL, 0, 0, 0);
 	if (err) {
 		status = AE_ERROR;
-		goto end_label;
+		goto out_list;
 	}
 	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
-					IPMI_TIMEOUT);
+					       IPMI_TIMEOUT);
 	acpi_format_ipmi_response(tx_msg, value, rem_time);
 	status = AE_OK;
 
-end_label:
+out_list:
 	mutex_lock(&ipmi_device->tx_msg_lock);
 	list_del(&tx_msg->head);
 	mutex_unlock(&ipmi_device->tx_msg_lock);
+out_msg:
 	kfree(tx_msg);
 	return status;
 }
Signature · 1 line
1.7.10
maintainer
#3 lore raw

Re: [PATCH 01/13] ACPI/IPMI: Fix potential response buffer overflow

Quoted 10 lines · Lv Zheng

This patch enhances sanity checks on message size to avoid potential buffer overflow.

The kernel IPMI message size is IPMI_MAX_MSG_LENGTH(272 bytes) while the ACPI specification defined IPMI message size is 64 bytes. The difference is not handled by the original codes. This may cause crash in the response handling codes.
This patch fixes this gap and also combines rx_data/tx_data to use single data/len pair since they need not be seperated.

Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |  100 ++++++++++++++++++++++++++++------------------
 1 file changed, 61 insertions(+), 39 deletions(-)

<formletter>

This is not the correct way to submit patches for inclusion in the stable kernel tree. Please read Documentation/stable_kernel_rules.txt for how to do this properly.

</formletter>

Same goes for the other patches you sent in this thread...

author
#4 lore raw

RE: [PATCH 01/13] ACPI/IPMI: Fix potential response buffer overflow

Quoted 24 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Greg KH
Sent: Tuesday, July 23, 2013 10:54 PM

Quoted 10 lines · Lv Zheng

This patch enhances sanity checks on message size to avoid potential buffer overflow.

The kernel IPMI message size is IPMI_MAX_MSG_LENGTH(272 bytes) while the ACPI specification defined IPMI message size is 64 bytes. The difference is not handled by the original codes. This may cause crash in the response handling codes.
This patch fixes this gap and also combines rx_data/tx_data to use single data/len pair since they need not be seperated.

Quoted 6 lines
---
 drivers/acpi/acpi_ipmi.c |  100
++++++++++++++++++++++++++++------------------
 1 file changed, 61 insertions(+), 39 deletions(-)

<formletter>

Quoted 7 lines

This is not the correct way to submit patches for inclusion in the stable kernel tree. Please read Documentation/stable_kernel_rules.txt
for how to do this properly.

</formletter>

Same goes for the other patches you sent in this thread...

OK, I'll add prerequisites for each that want to be accepted by the stable queue and re-send them (PATCH 01-06).

Thanks and best regards
-Lv

Quoted 4 lines

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

author
#5 lore raw

RE: [PATCH 01/13] ACPI/IPMI: Fix potential response buffer overflow

In reply to Greg KH
Quoted 31 lines

From: Zheng, Lv
Sent: Wednesday, July 24, 2013 8:22 AM

Quoted 28 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Greg KH
Sent: Tuesday, July 23, 2013 10:54 PM

Quoted 10 lines · Lv Zheng

This patch enhances sanity checks on message size to avoid potential buffer overflow.

The kernel IPMI message size is IPMI_MAX_MSG_LENGTH(272 bytes) while the ACPI specification defined IPMI message size is 64 bytes. The difference is not handled by the original codes. This may cause crash in the response handling codes.
This patch fixes this gap and also combines rx_data/tx_data to use single data/len pair since they need not be seperated.

Quoted 6 lines
---
 drivers/acpi/acpi_ipmi.c |  100
++++++++++++++++++++++++++++------------------
 1 file changed, 61 insertions(+), 39 deletions(-)

<formletter>

This is not the correct way to submit patches for inclusion in the stable kernel tree. Please read Documentation/stable_kernel_rules.txt for how to do this properly.

Quoted 6 lines
Quoted 3 lines

</formletter>

Same goes for the other patches you sent in this thread...

OK, I'll add prerequisites for each that want to be accepted by the stable queue and re-send them (PATCH 01-06).

Maybe I shouldn't.
I looks it is not possible to add commit ID prerequisites for patch series that has not been accepted by the mainline.
As the patches haven't been merged by the mainline, it is likely that the commit IDs in this series will be changed.
Please ignore [PATCH 01-06] that have been sent to the stable mailing list.
I'll just let ACPI maintainers know which patches I think that can go for stable tree and let they make the decision after the mainline acceptance.

Thanks and best regards
-Lv

Quoted 8 lines

Thanks and best regards
-Lv

Quoted 4 lines

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

author
#6 lore raw

PATCH 2/13 ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler()

In reply to Lv Zheng

This patch quick fixes the issues indicated by the test results that ipmi_msg_handler() is invoked in atomic context.

BUG: scheduling while atomic: kipmi0/18933/0x10000100
Modules linked in: ipmi_si acpi_ipmi ...
CPU: 3 PID: 18933 Comm: kipmi0 Tainted: G       AW    3.10.0-rc7+ #2
Hardware name: QCI QSSC-S4R/QSSC-S4R, BIOS QSSC-S4R.QCI.01.00.0027.070120100606 07/01/2010
 ffff8838245eea00 ffff88103fc63c98 ffffffff814c4a1e ffff88103fc63ca8
 ffffffff814bfbab ffff88103fc63d28 ffffffff814c73e0 ffff88103933cbd4
 0000000000000096 ffff88103fc63ce8 ffff88102f618000 ffff881035c01fd8
Call Trace:
 <IRQ>  [<ffffffff814c4a1e>] dump_stack+0x19/0x1b
 [<ffffffff814bfbab>] __schedule_bug+0x46/0x54
 [<ffffffff814c73e0>] __schedule+0x83/0x59c
 [<ffffffff81058853>] __cond_resched+0x22/0x2d
 [<ffffffff814c794b>] _cond_resched+0x14/0x1d
 [<ffffffff814c6d82>] mutex_lock+0x11/0x32
 [<ffffffff8101e1e9>] ? __default_send_IPI_dest_field.constprop.0+0x53/0x58
 [<ffffffffa09e3f9c>] ipmi_msg_handler+0x23/0x166 [ipmi_si]
 [<ffffffff812bf6e4>] deliver_response+0x55/0x5a
 [<ffffffff812c0fd4>] handle_new_recv_msgs+0xb67/0xc65
 [<ffffffff81007ad1>] ? read_tsc+0x9/0x19
 [<ffffffff814c8620>] ? _raw_spin_lock_irq+0xa/0xc
 [<ffffffffa09e1128>] ipmi_thread+0x5c/0x146 [ipmi_si]
 ...

Known issues:
- Replacing tx_msg_lock with spinlock is not performance friendly Current solution works but does not have the best performance because it is better to make atomic context run as fast as possible. Given there are no many IPMI messages created by ACPI, performance of current solution may be OK. It can be better via linking ipmi_recv_msg into an RX message queue and process it in other contexts.

---
 drivers/acpi/acpi_ipmi.c |   24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c +14 -10
index 28e2b4c..b37c189 100644
@@ -39,6 +39,7 @@
 #include <linux/ipmi.h>
 #include <linux/device.h>
 #include <linux/pnp.h>
+#include <linux/spinlock.h>
 
 MODULE_AUTHOR("Zhao Yakui");
 MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
@@ -58,7 +59,7 @@ struct acpi_ipmi_device {
 	struct list_head head;
 	/* the IPMI request message list */
 	struct list_head tx_msg_list;
-	struct mutex	tx_msg_lock;
+	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
 	struct pnp_dev *pnp_dev;
 	ipmi_user_t	user_interface;
@@ -146,6 +147,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	struct kernel_ipmi_msg *msg;
 	struct acpi_ipmi_buffer *buffer;
 	struct acpi_ipmi_device *device;
+	unsigned long flags;
 
 	msg = &tx_msg->tx_message;
 	/*
@@ -182,10 +184,10 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 
 	/* Get the msgid */
 	device = tx_msg->device;
-	mutex_lock(&device->tx_msg_lock);
+	spin_lock_irqsave(&device->tx_msg_lock, flags);
 	device->curr_msgid++;
 	tx_msg->tx_msgid = device->curr_msgid;
-	mutex_unlock(&device->tx_msg_lock);
+	spin_unlock_irqrestore(&device->tx_msg_lock, flags);
 
 	return 0;
 }
@@ -250,6 +252,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
 	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
 		dev_warn(&pnp_dev->dev,
@@ -258,14 +261,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		goto out_msg;
 	}
 
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
 		if (msg->msgid == tx_msg->tx_msgid) {
 			msg_found = 1;
 			break;
 		}
 	}
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
@@ -394,6 +397,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	struct acpi_ipmi_device *ipmi_device = handler_context;
 	int err, rem_time;
 	acpi_status status;
+	unsigned long flags;
 
 	/*
 	 * IPMI opregion message.
@@ -416,9 +420,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 		goto out_msg;
 	}
 
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	err = ipmi_request_settime(ipmi_device->user_interface,
 				   &tx_msg->addr,
 				   tx_msg->tx_msgid,
@@ -434,9 +438,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	status = AE_OK;
 
 out_list:
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_del(&tx_msg->head);
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	kfree(tx_msg);
 	return status;
@@ -479,7 +483,7 @@ static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
 
 	INIT_LIST_HEAD(&ipmi_device->head);
 
-	mutex_init(&ipmi_device->tx_msg_lock);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
 	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
 	ipmi_install_space_handler(ipmi_device);
 
Signature · 1 line
1.7.10
author
#7 lore raw

PATCH 3/13 ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

In reply to Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
 drivers/acpi/acpi_ipmi.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +8 -2
index b37c189..527ee43 100644
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -286,10 +288,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
+out_lock:
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }
Signature · 1 line
1.7.10
#8 lore raw

Re: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 59 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
 drivers/acpi/acpi_ipmi.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +4 -2
index b37c189..527ee43 100644
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +2 -0
@@ -286,10 +288,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 301

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +2 -0
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
+out_lock:
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }

Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#9 lore raw

RE: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

-stable according to the previous conversation.

Quoted 74 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 29 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
 drivers/acpi/acpi_ipmi.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 14 lines
drivers/acpi/acpi_ipmi.c +1 -2
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */ @@ -286,10 +288,14 @@
Quoted 1 line

static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +2 -0
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is used or not by the other side codes under the condition set after the smp_wmb().

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*

192          * value is also used as output parameter. It represents the response
193          * IPMI message returned by IPMI command.
194          */

195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*

201          * If the flag of msg_done is not set or the recv length is zero, it
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*

210          * If the IPMI response message is obtained correctly, the status code
211          * will be ACPI_IPMI_OK
212          */
213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from msg->rx_data.
Note that the rx_len is 0 during initialization and will never exceed the sizeof(buffer->data), so the read is safe.

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and
2. the smp_rmb()/smp_wmb() added in this patch will be deleted in [PATCH 07].

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Thanks and best regards
-Lv

Quoted 14 lines

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 301

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +2 -0
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
+out_lock:
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }

Rafael

Quoted 3 lines

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk

#10 lore raw

Re: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 124 lines · Lv

-stable according to the previous conversation.

Quoted 74 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 29 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
 drivers/acpi/acpi_ipmi.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 14 lines
drivers/acpi/acpi_ipmi.c +1 -2
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */ @@ -286,10 +288,14 @@
Quoted 1 line

static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +2 -0
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is used or not by the other side codes under the condition set after the smp_wmb().

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*

192          * value is also used as output parameter. It represents the response
193          * IPMI message returned by IPMI command.
194          */

195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*

201          * If the flag of msg_done is not set or the recv length is zero, it
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*

210          * If the IPMI response message is obtained correctly, the status code
211          * will be ACPI_IPMI_OK
212          */
213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from msg->rx_data.
Note that the rx_len is 0 during initialization and will never exceed the sizeof(buffer->data), so the read is safe.

Quoted 8 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and
2. the smp_rmb()/smp_wmb() added in this patch will be deleted in [PATCH 07].

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need them at all.

Thanks,
Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
#11 lore raw

Re: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 131 lines · Rafael J. Wysocki
Quoted 122 lines · Lv

-stable according to the previous conversation.

Quoted 73 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 29 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
  drivers/acpi/acpi_ipmi.c |   10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
  	struct acpi_ipmi_msg *tx_msg, *temp;
  	int count = HZ / 10;
  	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
  	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);
  	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
  	/* wait for about 100ms to flush the tx message list */
  	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 14 lines
drivers/acpi/acpi_ipmi.c +1 -2
  			break;
  		}
  	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
  	if (!msg_found) {
  		dev_warn(&pnp_dev->dev,
  			 "Unexpected response (msg id %ld) is returned.\n",
  			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
  	}
 
  	/* copy the response data to Rx_data buffer */ @@ -286,10 +288,14 @@
Quoted 1 line

static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
  	}
  	tx_msg->rx_len = msg->msg.data_len;
  	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
drivers/acpi/acpi_ipmi.c +2 -0
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is used or not by the other side codes under the condition set after the smp_wmb().

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*

192          * value is also used as output parameter. It represents the response
193          * IPMI message returned by IPMI command.
194          */

195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*

201          * If the flag of msg_done is not set or the recv length is zero, it
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*

210          * If the IPMI response message is obtained correctly, the status code
211          * will be ACPI_IPMI_OK
212          */
213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from msg->rx_data.
Note that the rx_len is 0 during initialization and will never exceed the sizeof(buffer->data), so the read is safe.

Quoted 8 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and
2. the smp_rmb()/smp_wmb() added in this patch will be deleted in [PATCH 07].

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Quoted 8 lines

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need them at all.

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                        IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

-corey

Quoted 5 lines

Thanks,
Rafael

#12 lore raw

Re: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 141 lines · Corey Minyard
Quoted 131 lines · Rafael J. Wysocki
Quoted 122 lines · Lv

-stable according to the previous conversation.

Quoted 73 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 29 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
  drivers/acpi/acpi_ipmi.c |   10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
  	struct acpi_ipmi_msg *tx_msg, *temp;
  	int count = HZ / 10;
  	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
  	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);
  	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
  	/* wait for about 100ms to flush the tx message list */
  	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 14 lines
drivers/acpi/acpi_ipmi.c +1 -2
  			break;
  		}
  	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
  	if (!msg_found) {
  		dev_warn(&pnp_dev->dev,
  			 "Unexpected response (msg id %ld) is returned.\n",
  			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
  	}
 
  	/* copy the response data to Rx_data buffer */ @@ -286,10 +288,14 @@
Quoted 1 line

static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
  	}
  	tx_msg->rx_len = msg->msg.data_len;
  	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
drivers/acpi/acpi_ipmi.c +2 -0
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is used or not by the other side codes under the condition set after the smp_wmb().

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*

192          * value is also used as output parameter. It represents the response
193          * IPMI message returned by IPMI command.
194          */

195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*

201          * If the flag of msg_done is not set or the recv length is zero, it
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*

210          * If the IPMI response message is obtained correctly, the status code
211          * will be ACPI_IPMI_OK
212          */
213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from msg->rx_data.
Note that the rx_len is 0 during initialization and will never exceed the sizeof(buffer->data), so the read is safe.

Quoted 8 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and
2. the smp_rmb()/smp_wmb() added in this patch will be deleted in [PATCH 07].

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Quoted 8 lines

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need them at all.

Quoted 8 lines

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                         IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

I believe so.

Quoted 6 lines

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

Good point.

Quoted 3 lines

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

Lv, what about this?

Rafael

author
#13 lore raw

RE: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 181 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Rafael J. Wysocki
Sent: Friday, July 26, 2013 3:33 AM

Quoted 39 lines · Corey Minyard
Quoted 38 lines · Rafael J. Wysocki
Quoted 37 lines · Lv

-stable according to the previous conversation.

Quoted 35 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 30 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
  drivers/acpi/acpi_ipmi.c |   10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +0 -0

index
b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)

Quoted 7 lines
drivers/acpi/acpi_ipmi.c +2 -0
  	struct acpi_ipmi_msg *tx_msg, *temp;
  	int count = HZ / 10;
  	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
  	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list,

head) {

Quoted 33 lines
Quoted 33 lines
Quoted 33 lines
Quoted 33 lines

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 8 lines
drivers/acpi/acpi_ipmi.c +1 -0
  		/* wake up the sleep thread on the Tx msg */
  		complete(&tx_msg->tx_complete);
  	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
  	/* wait for about 100ms to flush the tx message list */
  	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +2 -2
  			break;
  		}
  	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
  	if (!msg_found) {
  		dev_warn(&pnp_dev->dev,
  			 "Unexpected response (msg id %ld) is returned.\n",
  			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
  	}
 
  	/* copy the response data to Rx_data buffer */ @@ -286,10
+288,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg
Quoted 1 line

*msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
  	}
  	tx_msg->rx_len = msg->msg.data_len;
  	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
drivers/acpi/acpi_ipmi.c +2 -0
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is

used or not by the other side codes under the condition set after the smp_wmb().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*
192 * value is also used as output parameter. It represents the

response

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

193 * IPMI message returned by IPMI command.
194 */
195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*
201 * If the flag of msg_done is not set or the recv length is

zero, it

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*
210 * If the IPMI response message is obtained correctly, the

status code

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

211 * will be ACPI_IPMI_OK
212 */

213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from

msg->rx_data.

Quoted 1 line
Quoted 1 line
Quoted 1 line

Note that the rx_len is 0 during initialization and will never exceed the

sizeof(buffer->data), so the read is safe.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and 2. the smp_rmb()/smp_wmb() added in this patch will

be deleted in [PATCH 07].

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes

running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Quoted 8 lines
Quoted 8 lines

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need

them at all.

Quoted 9 lines

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                         IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

I believe so.

Quoted 6 lines

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

Quoted 7 lines

Good point.

Quoted 3 lines

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

Lv, what about this?

Please refer to my reply to Corey's comment. :-)

Thanks and best regards
-Lv

Quoted 7 lines

Rafael

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

author
#14 lore raw

RE: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

In reply to Corey Minyard
Quoted 169 lines

From: Corey Minyard [mailto:[email protected]]
Sent: Friday, July 26, 2013 2:13 AM

Quoted 78 lines · Rafael J. Wysocki
Quoted 77 lines · Lv

-stable according to the previous conversation.

Quoted 75 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 30 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
  drivers/acpi/acpi_ipmi.c |   10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +0 -0

index
b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
  	struct acpi_ipmi_msg *tx_msg, *temp;
  	int count = HZ / 10;
  	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
  	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);
  	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
  	/* wait for about 100ms to flush the tx message list */
  	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +2 -2
  			break;
  		}
  	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
  	if (!msg_found) {
  		dev_warn(&pnp_dev->dev,
  			 "Unexpected response (msg id %ld) is returned.\n",
  			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
  	}
 
  	/* copy the response data to Rx_data buffer */ @@ -286,10
+288,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg,
Quoted 1 line

void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
  	}
  	tx_msg->rx_len = msg->msg.data_len;
  	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
drivers/acpi/acpi_ipmi.c +2 -0
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is

used or not by the other side codes under the condition set after the smp_wmb().

Quoted 8 lines
Quoted 8 lines

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

Quoted 7 lines
Quoted 7 lines

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*
192 * value is also used as output parameter. It represents the

response

Quoted 9 lines
Quoted 9 lines

193 * IPMI message returned by IPMI command.
194 */
195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*
201 * If the flag of msg_done is not set or the recv length is zero,

it

Quoted 10 lines
Quoted 10 lines
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*
210 * If the IPMI response message is obtained correctly, the

status code

Quoted 8 lines
Quoted 8 lines

211 * will be ACPI_IPMI_OK
212 */

213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from

msg->rx_data.

Quoted 1 line
Quoted 1 line

Note that the rx_len is 0 during initialization and will never exceed the

sizeof(buffer->data), so the read is safe.

Quoted 4 lines
Quoted 4 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and 2. the smp_rmb()/smp_wmb() added in this patch will be

deleted in [PATCH 07].

Quoted 5 lines
Quoted 5 lines

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes

running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Quoted 8 lines

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need

them at all.

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                         IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

Quoted 6 lines

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

It's just difficult for me to determine retry count and timeout value, maybe retry=0, timeout=IPMI_TIMEOUT is OK. The code of the timeout completion is already there, I think the quick fix code should not introduce this logic. I'll add a new patch to apply your comment.

Quoted 4 lines

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

PATCH 07 fixed this issue.
Here we just need the smp_rmb() or holding tx_msg_lock() around the acpi_format_ipmi_response().

Thanks for commenting.

Best regards
-Lv

Quoted 8 lines

-corey

Quoted 5 lines

Thanks,
Rafael

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk

#15 lore raw

Re: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 16 lines · Lv
Quoted 10 lines

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                          IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

Quoted 6 lines

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

Quoted 3 lines

It's just difficult for me to determine retry count and timeout value, maybe retry=0, timeout=IPMI_TIMEOUT is OK. The code of the timeout completion is already there, I think the quick fix code should not introduce this logic. I'll add a new patch to apply your comment.

Since it is a local BMC, I doubt a retry is required. That is probably fine. Or you could set retry=1 and timeout=IPMI_TIMEOUT/2 if you wanted to be more sure, but I doubt it would make a difference. The only time you really need to worry about retries is if you are resetting the BMC or it is being overloaded.

Quoted 6 lines
Quoted 4 lines

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

PATCH 07 fixed this issue.
Here we just need the smp_rmb() or holding tx_msg_lock() around the acpi_format_ipmi_response().

If you apply the fix like I suggest, then the race goes away. If there's no timeout and it just waits for the completion, things get a lot simpler.

Quoted 2 lines

Thanks for commenting.

No problem, thanks for working on this.

-corey

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk

author
#16 lore raw

RE: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

Quoted 22 lines

From: Corey Minyard [mailto:[email protected]]
Sent: Friday, July 26, 2013 8:48 AM

Quoted 10 lines · Lv
Quoted 10 lines

If I understand this correctly, the problem would be if:

rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,

                                          IPMI_TIMEOUT);

returns on a timeout, then checks msg_done and races with something setting msg_done. If that is the case, you would need the smp_rmb() before checking msg_done.

Quoted 7 lines
Quoted 6 lines

However, the timeout above is unnecessary. You are using ipmi_request_settime(), so you can set the timeout when the IPMI command fails and returns a failure message. The driver guarantees a return message for each request. Just remove the timeout from the completion, set the timeout and retries in the ipmi request, and the completion should handle the barrier issues.

It's just difficult for me to determine retry count and timeout value, maybe

retry=0, timeout=IPMI_TIMEOUT is OK.

Quoted 8 lines
Quoted 1 line

The code of the timeout completion is already there, I think the quick fix code

should not introduce this logic.

Quoted 1 line

I'll add a new patch to apply your comment.

Since it is a local BMC, I doubt a retry is required. That is probably fine. Or you could set retry=1 and timeout=IPMI_TIMEOUT/2 if you wanted to be more sure, but I doubt it would make a difference. The only time you really need to worry about retries is if you are resetting the BMC or it is being overloaded.

I think for ACPI IPMI operation region, retries can be implemented in the ASL codes by the BIOS. I'll check if retry=0 is correct.

Quoted 11 lines
Quoted 7 lines
Quoted 5 lines

Plus, from a quick glance at the code, it doesn't look like it will properly handle a situation where the timeout occurs and is handled then the response comes in later.

PATCH 07 fixed this issue.
Here we just need the smp_rmb() or holding tx_msg_lock() around the

acpi_format_ipmi_response().

If you apply the fix like I suggest, then the race goes away. If there's no timeout and it just waits for the completion, things get a lot simpler.

Exactly. I'll try to apply this in this patch, then the PATCH 07 is also need to be re-worked.

Thanks and best regards
-Lv

Quoted 6 lines
Quoted 2 lines

Thanks for commenting.

No problem, thanks for working on this.

-corey

author
#17 lore raw

RE: [PATCH 03/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

In reply to Rafael J. Wysocki
Quoted 161 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 8:07 PM

Quoted 78 lines · Lv

-stable according to the previous conversation.

Quoted 76 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, July 25, 2013 7:38 AM

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 30 lines · Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.
Calling smp_wmb() before setting msg_done flag so that messages completed due to flushing will not be handled as 'done' messages while their contents are not vaild.

---
 drivers/acpi/acpi_ipmi.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +0 -0

index
b37c189..527ee43 100644

drivers/acpi/acpi_ipmi.c +0 -0
@@ -230,11 +230,14 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +3 -0
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -268,13 +271,12 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +2 -2
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */ @@ -286,10
+288,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg,
Quoted 1 line

void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) · line 292

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +2 -0
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	/* tx_msg content must be valid before setting msg_done flag */
+	smp_wmb();

That's suspicious.

Quoted 4 lines
Quoted 2 lines

If you need the write barrier here, you'll most likely need a read barrier somewhere else. Where's that?

It might depend on whether the content written before the smp_wmb() is

used or not by the other side codes under the condition set after the smp_wmb().

Quoted 8 lines

So comment could be treated as 2 parts:
1. do we need a paired smp_rmb().
2. do we need a smp_wmb().

For 1.
If we want a paired smp_rmb(), then it will appear in this function:

Quoted 7 lines

186 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
187 acpi_integer *value, int rem_time)
188 {
189 struct acpi_ipmi_buffer *buffer;
190
191 /*
192 * value is also used as output parameter. It represents the

response

Quoted 9 lines

193 * IPMI message returned by IPMI command.
194 */
195 buffer = (struct acpi_ipmi_buffer *)value;
196 if (!rem_time && !msg->msg_done) {
197 buffer->status = ACPI_IPMI_TIMEOUT;
198 return;
199 }
200 /*
201 * If the flag of msg_done is not set or the recv length is zero,

it

Quoted 10 lines
202          * means that the IPMI command is not executed correctly.
203          * The status code will be ACPI_IPMI_UNKNOWN.
204          */

205 if (!msg->msg_done || !msg->rx_len) {
206 buffer->status = ACPI_IPMI_UNKNOWN;
207 return;
208 }
+ smp_rmb();
209 /*
210 * If the IPMI response message is obtained correctly, the

status code

Quoted 8 lines

211 * will be ACPI_IPMI_OK
212 */

213         buffer->status = ACPI_IPMI_OK;
214         buffer->length = msg->rx_len;
215         memcpy(buffer->data, msg->rx_data, msg->rx_len);

216 }

If we don't then there will only be msg content not correctly read from

msg->rx_data.

Quoted 1 line

Note that the rx_len is 0 during initialization and will never exceed the

sizeof(buffer->data), so the read is safe.

Quoted 4 lines

Being without smp_rmb() is also OK in this case, since:
1. buffer->data will never be used when buffer->status is not ACPI_IPMI_OK and 2. the smp_rmb()/smp_wmb() added in this patch will be

deleted in [PATCH 07].

Quoted 5 lines

So IMO, we needn't add the smp_rmb(), what do you think of this?

For 2.
If we don't add smp_wmb() in the ipmi_msg_handler(), then the codes

running on other thread in the acpi_format_ipmi_response() may read wrong msg->rx_data (a timeout triggers this function, but when acpi_format_ipmi_response() is entered, the msg->msg_done flag could be seen as 1 but the msg->rx_data is not ready), this is what we want to avoid in this quick fix.

Using smp_wmb() without the complementary smp_rmb() doesn't makes sense, because each of them prevents only one flow of control from being speculatively reordered, either by the CPU or by the compiler. If only one of them is used without the other, then the flow of control without the barrier may be reordered in a way that will effectively cancel the effect of the barrier in the second flow of control.

Quoted 2 lines

So, either we need *both* smp_wmb() and smp_rmb(), or we don't need them at all.

I think you are right, it is about order of L1 cache flushing.

The smp_wmb()/smp_rmb() is not a useful approach for non-tuning implementations.

It's here because the code used a combined programing model, and thus a bug.
Such bugs can be avoided by:
1. either using bigger granularity locks, in this case, tx_msg_lock should be held around acpi_format_ipmi_response()
2. or using smaller granularity locks, races are automatically avoided by the excluded running flows (like what the PATCH 07 shows)

I'll update this patch or even drop it.

Thanks and best regards
-Lv

Quoted 8 lines

Thanks,
Rafael

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

author
#18 lore raw

PATCH 4/13 ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user

In reply to Lv Zheng

This patch uses reference counting to fix the race caused by the unprotected ACPI IPMI user.

As the acpi_ipmi_device->user_interface check in acpi_ipmi_space_handler() can happen before setting user_interface to NULL and codes after the check in acpi_ipmi_space_handler() can happen after user_interface becoming NULL, then the on-going acpi_ipmi_space_handler() still can pass an invalid acpi_ipmi_device->user_interface to ipmi_request_settime(). Such race condition is not allowed by the IPMI layer's API design as crash will happen in ipmi_request_settime().
In IPMI layer, smi_gone()/new_smi() callbacks are protected by smi_watchers_mutex, thus their invocations are serialized. But as a new smi can re-use the freed intf_num, it requires that the callback implementation must not use intf_num as an identification mean or it must ensure all references to the previous smi are all dropped before exiting smi_gone() callback. In case of acpi_ipmi module, this means ipmi_flush_tx_msg() must ensure all on-going IPMI transfers are completed before exiting ipmi_flush_tx_msg().

This patch follows ipmi_devintf.c design:
1. Invoking ipmi_destroy_user() after the reference count of acpi_ipmi_device dropping to 0, this matches IPMI layer's API calling rule on ipmi_destroy_user() and ipmi_request_settime().
2. References of acpi_ipmi_device dropping to 1 means tx_msg related to this acpi_ipmi_device are all freed, this can be used to implement the new flushing mechanism. Note complete() must be retried so that the on-going tx_msg won't block flushing at the point to add tx_msg into tx_msg_list where reference of acpi_ipmi_device is held. This matches the IPMI layer's callback rule on smi_gone()/new_smi() serialization.
3. ipmi_flush_tx_msg() is performed after deleting acpi_ipmi_device from the list so that no new tx_msg can be created after entering flushing process.
4. The flushing of tx_msg is also moved out of ipmi_lock in this patch.

The forthcoming IPMI operation region handler installation changes also requires acpi_ipmi_device be handled in the reference counting style.

Authorship is also updated due to this design change.

---
 drivers/acpi/acpi_ipmi.c |  249 +++++++++++++++++++++++++++-------------------
 1 file changed, 149 insertions(+), 100 deletions(-)
drivers/acpi/acpi_ipmi.c +149 -100
index 527ee43..cbf25e0 100644
@@ -1,8 +1,9 @@
 /*
  *  acpi_ipmi.c - ACPI IPMI opregion
  *
- *  Copyright (C) 2010 Intel Corporation
- *  Copyright (C) 2010 Zhao Yakui <[email protected]>
+ *  Copyright (C) 2010, 2013 Intel Corporation
+ *    Author: Zhao Yakui <[email protected]>
+ *            Lv Zheng <[email protected]>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
@@ -67,6 +68,7 @@ struct acpi_ipmi_device {
 	long curr_msgid;
 	unsigned long flags;
 	struct ipmi_smi_info smi_data;
+	atomic_t refcnt;
 };
 
 struct ipmi_driver_data {
@@ -107,8 +109,8 @@ struct acpi_ipmi_buffer {
 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 void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device);
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device);
+static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
+static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -122,6 +124,80 @@ static struct ipmi_driver_data driver_data = {
 	},
 };
 
+static struct acpi_ipmi_device *
+ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+{
+	struct acpi_ipmi_device *ipmi_device;
+	int err;
+	ipmi_user_t user;
+
+	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+	if (!ipmi_device)
+		return NULL;
+
+	atomic_set(&ipmi_device->refcnt, 1);
+	INIT_LIST_HEAD(&ipmi_device->head);
+	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
+
+	ipmi_device->handle = handle;
+	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
+	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->ipmi_ifnum = iface;
+
+	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+			       ipmi_device, &user);
+	if (err) {
+		put_device(smi_data->dev);
+		kfree(ipmi_device);
+		return NULL;
+	}
+	ipmi_device->user_interface = user;
+	ipmi_install_space_handler(ipmi_device);
+
+	return ipmi_device;
+}
+
+static struct acpi_ipmi_device *
+acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device)
+{
+	if (ipmi_device)
+		atomic_inc(&ipmi_device->refcnt);
+	return ipmi_device;
+}
+
+static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
+{
+	ipmi_remove_space_handler(ipmi_device);
+	ipmi_destroy_user(ipmi_device->user_interface);
+	put_device(ipmi_device->smi_data.dev);
+	kfree(ipmi_device);
+}
+
+static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)
+{
+	if (ipmi_device && atomic_dec_and_test(&ipmi_device->refcnt))
+		ipmi_dev_release(ipmi_device);
+}
+
+static struct acpi_ipmi_device *acpi_ipmi_get_targeted_smi(int iface)
+{
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
+
+	mutex_lock(&driver_data.ipmi_lock);
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {
+			dev_found = 1;
+			acpi_ipmi_dev_get(ipmi_device);
+			break;
+		}
+	}
+	mutex_unlock(&driver_data.ipmi_lock);
+
+	return dev_found ? ipmi_device : NULL;
+}
+
 static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *ipmi_msg;
@@ -228,25 +304,24 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *tx_msg, *temp;
-	int count = HZ / 10;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 	unsigned long flags;
 
-	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-	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);
-	}
-	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
-
-	/* wait for about 100ms to flush the tx message list */
-	while (count--) {
-		if (list_empty(&ipmi->tx_msg_list))
-			break;
-		schedule_timeout(1);
+	/*
+	 * NOTE: Synchronous Flushing
+	 * Wait until refnct dropping to 1 - no other users unless this
+	 * context.  This function should always be called before
+	 * acpi_ipmi_device destruction.
+	 */
+	while (atomic_read(&ipmi->refcnt) > 1) {
+		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+		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);
+		}
+		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+		schedule_timeout_uninterruptible(msecs_to_jiffies(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)
@@ -304,22 +379,26 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
 	struct pnp_dev *pnp_dev;
-	ipmi_user_t		user;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
 
 	err = ipmi_get_smi_info(iface, &smi_data);
-
 	if (err)
 		return;
 
-	if (smi_data.addr_src != SI_ACPI) {
-		put_device(smi_data.dev);
-		return;
-	}
-
+	if (smi_data.addr_src != SI_ACPI)
+		goto err_ref;
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
+	if (!handle)
+		goto err_ref;
+	pnp_dev = to_pnp_dev(smi_data.dev);
+
+	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	if (!ipmi_device) {
+		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		goto err_ref;
+	}
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
@@ -328,54 +407,42 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 		 * to the device list, don't add it again.
 		 */
 		if (temp->handle == handle)
-			goto out;
+			goto err_lock;
 	}
 
-	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
-
-	if (!ipmi_device)
-		goto out;
-
-	pnp_dev = to_pnp_dev(smi_data.dev);
-	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = pnp_dev;
-
-	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
-					ipmi_device, &user);
-	if (err) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
-		kfree(ipmi_device);
-		goto out;
-	}
-	acpi_add_ipmi_device(ipmi_device);
-	ipmi_device->user_interface = user;
-	ipmi_device->ipmi_ifnum = iface;
+	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
-	memcpy(&ipmi_device->smi_data, &smi_data, sizeof(struct ipmi_smi_info));
+	put_device(smi_data.dev);
 	return;
 
-out:
+err_lock:
 	mutex_unlock(&driver_data.ipmi_lock);
+	ipmi_dev_release(ipmi_device);
+err_ref:
 	put_device(smi_data.dev);
 	return;
 }
 
 static void ipmi_bmc_gone(int iface)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
 
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		if (ipmi_device->ipmi_ifnum != iface)
-			continue;
-
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
-		break;
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {
+			dev_found = 1;
+			break;
+		}
 	}
+	if (dev_found)
+		list_del(&ipmi_device->head);
 	mutex_unlock(&driver_data.ipmi_lock);
+
+	if (dev_found) {
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+	}
 }
 
 /* --------------------------------------------------------------------------
@@ -400,7 +467,8 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 			void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
-	struct acpi_ipmi_device *ipmi_device = handler_context;
+	int iface = (long)handler_context;
+	struct acpi_ipmi_device *ipmi_device;
 	int err, rem_time;
 	acpi_status status;
 	unsigned long flags;
@@ -414,12 +482,15 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	if (!ipmi_device->user_interface)
+	ipmi_device = acpi_ipmi_get_targeted_smi(iface);
+	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
 	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg)
-		return AE_NO_MEMORY;
+	if (!tx_msg) {
+		status = AE_NO_MEMORY;
+		goto out_ref;
+	}
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
 		status = AE_TYPE;
@@ -449,6 +520,8 @@ out_list:
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	kfree(tx_msg);
+out_ref:
+	acpi_ipmi_dev_put(ipmi_device);
 	return status;
 }
 
@@ -473,7 +546,7 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 	status = acpi_install_address_space_handler(ipmi->handle,
 						    ACPI_ADR_SPACE_IPMI,
 						    &acpi_ipmi_space_handler,
-						    NULL, ipmi);
+						    NULL, (void *)((long)ipmi->ipmi_ifnum));
 	if (ACPI_FAILURE(status)) {
 		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
@@ -484,36 +557,6 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 	return 0;
 }
 
-static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-
-	INIT_LIST_HEAD(&ipmi_device->head);
-
-	spin_lock_init(&ipmi_device->tx_msg_lock);
-	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
-	ipmi_install_space_handler(ipmi_device);
-
-	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
-}
-
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-	/*
-	 * If the IPMI user interface is created, it should be
-	 * destroyed.
-	 */
-	if (ipmi_device->user_interface) {
-		ipmi_destroy_user(ipmi_device->user_interface);
-		ipmi_device->user_interface = NULL;
-	}
-	/* flush the Tx_msg list */
-	if (!list_empty(&ipmi_device->tx_msg_list))
-		ipmi_flush_tx_msg(ipmi_device);
-
-	list_del(&ipmi_device->head);
-	ipmi_remove_space_handler(ipmi_device);
-}
-
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
@@ -530,7 +573,7 @@ static int __init acpi_ipmi_init(void)
 
 static void __exit acpi_ipmi_exit(void)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	struct acpi_ipmi_device *ipmi_device;
 
 	if (acpi_disabled)
 		return;
@@ -544,11 +587,17 @@ static void __exit acpi_ipmi_exit(void)
 	 * handler and free it.
 	 */
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
+	while (!list_empty(&driver_data.ipmi_devices)) {
+		ipmi_device = list_first_entry(&driver_data.ipmi_devices,
+					       struct acpi_ipmi_device,
+					       head);
+		list_del(&ipmi_device->head);
+		mutex_unlock(&driver_data.ipmi_lock);
+
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+
+		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 }
Signature · 1 line
1.7.10
#19 lore raw

Re: [PATCH 04/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user

Quoted 63 lines · Lv Zheng

This patch uses reference counting to fix the race caused by the unprotected ACPI IPMI user.

As the acpi_ipmi_device->user_interface check in acpi_ipmi_space_handler() can happen before setting user_interface to NULL and codes after the check in acpi_ipmi_space_handler() can happen after user_interface becoming NULL, then the on-going acpi_ipmi_space_handler() still can pass an invalid acpi_ipmi_device->user_interface to ipmi_request_settime(). Such race condition is not allowed by the IPMI layer's API design as crash will happen in ipmi_request_settime().
In IPMI layer, smi_gone()/new_smi() callbacks are protected by smi_watchers_mutex, thus their invocations are serialized. But as a new smi can re-use the freed intf_num, it requires that the callback implementation must not use intf_num as an identification mean or it must ensure all references to the previous smi are all dropped before exiting smi_gone() callback. In case of acpi_ipmi module, this means ipmi_flush_tx_msg() must ensure all on-going IPMI transfers are completed before exiting ipmi_flush_tx_msg().

This patch follows ipmi_devintf.c design:
1. Invoking ipmi_destroy_user() after the reference count of acpi_ipmi_device dropping to 0, this matches IPMI layer's API calling rule on ipmi_destroy_user() and ipmi_request_settime().
2. References of acpi_ipmi_device dropping to 1 means tx_msg related to this acpi_ipmi_device are all freed, this can be used to implement the new flushing mechanism. Note complete() must be retried so that the on-going tx_msg won't block flushing at the point to add tx_msg into tx_msg_list where reference of acpi_ipmi_device is held. This matches the IPMI layer's callback rule on smi_gone()/new_smi() serialization.
3. ipmi_flush_tx_msg() is performed after deleting acpi_ipmi_device from the list so that no new tx_msg can be created after entering flushing process.
4. The flushing of tx_msg is also moved out of ipmi_lock in this patch.

The forthcoming IPMI operation region handler installation changes also requires acpi_ipmi_device be handled in the reference counting style.

Authorship is also updated due to this design change.

---
 drivers/acpi/acpi_ipmi.c |  249 +++++++++++++++++++++++++++-------------------
 1 file changed, 149 insertions(+), 100 deletions(-)
drivers/acpi/acpi_ipmi.c +3 -2
index 527ee43..cbf25e0 100644
@@ -1,8 +1,9 @@
 /*
  *  acpi_ipmi.c - ACPI IPMI opregion
  *
- *  Copyright (C) 2010 Intel Corporation
- *  Copyright (C) 2010 Zhao Yakui <[email protected]>
+ *  Copyright (C) 2010, 2013 Intel Corporation
+ *    Author: Zhao Yakui <[email protected]>
+ *            Lv Zheng <[email protected]>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *

drivers/acpi/acpi_ipmi.c struct acpi_ipmi_device { · line 71

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -0
@@ -67,6 +68,7 @@ struct acpi_ipmi_device {
 	long curr_msgid;
 	unsigned long flags;
 	struct ipmi_smi_info smi_data;
+	atomic_t refcnt;

Can you use a kref instead?

Quoted 69 lines
drivers/acpi/acpi_ipmi.c +53 -2
 };
 
 struct ipmi_driver_data {
@@ -107,8 +109,8 @@ struct acpi_ipmi_buffer {
 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 void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device);
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device);
+static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
+static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -122,6 +124,80 @@ static struct ipmi_driver_data driver_data = {
 	},
 };
 
+static struct acpi_ipmi_device *
+ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+{
+	struct acpi_ipmi_device *ipmi_device;
+	int err;
+	ipmi_user_t user;
+
+	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+	if (!ipmi_device)
+		return NULL;
+
+	atomic_set(&ipmi_device->refcnt, 1);
+	INIT_LIST_HEAD(&ipmi_device->head);
+	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
+
+	ipmi_device->handle = handle;
+	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
+	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->ipmi_ifnum = iface;
+
+	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+			       ipmi_device, &user);
+	if (err) {
+		put_device(smi_data->dev);
+		kfree(ipmi_device);
+		return NULL;
+	}
+	ipmi_device->user_interface = user;
+	ipmi_install_space_handler(ipmi_device);
+
+	return ipmi_device;
+}
+
+static struct acpi_ipmi_device *
+acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device)
+{
+	if (ipmi_device)
+		atomic_inc(&ipmi_device->refcnt);
+	return ipmi_device;
+}
+
+static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
+{
+	ipmi_remove_space_handler(ipmi_device);
+	ipmi_destroy_user(ipmi_device->user_interface);
+	put_device(ipmi_device->smi_data.dev);
+	kfree(ipmi_device);
+}
+
+static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 187

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +10 -0
+{
+	if (ipmi_device && atomic_dec_and_test(&ipmi_device->refcnt))
+		ipmi_dev_release(ipmi_device);
+}
+
+static struct acpi_ipmi_device *acpi_ipmi_get_targeted_smi(int iface)
+{
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
+

Why don't you do

	struct acpi_ipmi_device *ipmi_device, *ret = NULL;

and then ->

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 190

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +3 -0
+	mutex_lock(&driver_data.ipmi_lock);
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {

-> ret = ipmi_device; ->

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 198

Quoted 8 lines
drivers/acpi/acpi_ipmi.c +8 -0
+			dev_found = 1;
+			acpi_ipmi_dev_get(ipmi_device);
+			break;
+		}
+	}
+	mutex_unlock(&driver_data.ipmi_lock);
+
+	return dev_found ? ipmi_device : NULL;

-> return ret;

Quoted 22 lines
drivers/acpi/acpi_ipmi.c +2 -11
+}
+
 static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *ipmi_msg;
@@ -228,25 +304,24 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *tx_msg, *temp;
-	int count = HZ / 10;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 	unsigned long flags;
 
-	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-	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);
-	}
-	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
-
-	/* wait for about 100ms to flush the tx message list */
-	while (count--) {

drivers/acpi/acpi_ipmi.c static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, · line 315

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +7 -3
-		if (list_empty(&ipmi->tx_msg_list))
-			break;
-		schedule_timeout(1);
+	/*
+	 * NOTE: Synchronous Flushing
+	 * Wait until refnct dropping to 1 - no other users unless this
+	 * context.  This function should always be called before
+	 * acpi_ipmi_device destruction.
+	 */
+	while (atomic_read(&ipmi->refcnt) > 1) {

Isn't this racy? What if we see that the refcount is 1 and break the loop, but someone else bumps up the refcount at the same time?

Quoted 99 lines
drivers/acpi/acpi_ipmi.c +27 -34
+		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+		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);
+		}
+		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+		schedule_timeout_uninterruptible(msecs_to_jiffies(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)
@@ -304,22 +379,26 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
 	struct pnp_dev *pnp_dev;
-	ipmi_user_t		user;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
 
 	err = ipmi_get_smi_info(iface, &smi_data);
-
 	if (err)
 		return;
 
-	if (smi_data.addr_src != SI_ACPI) {
-		put_device(smi_data.dev);
-		return;
-	}
-
+	if (smi_data.addr_src != SI_ACPI)
+		goto err_ref;
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
+	if (!handle)
+		goto err_ref;
+	pnp_dev = to_pnp_dev(smi_data.dev);
+
+	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	if (!ipmi_device) {
+		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		goto err_ref;
+	}
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
@@ -328,54 +407,42 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 		 * to the device list, don't add it again.
 		 */
 		if (temp->handle == handle)
-			goto out;
+			goto err_lock;
 	}
 
-	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
-
-	if (!ipmi_device)
-		goto out;
-
-	pnp_dev = to_pnp_dev(smi_data.dev);
-	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = pnp_dev;
-
-	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
-					ipmi_device, &user);
-	if (err) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
-		kfree(ipmi_device);
-		goto out;
-	}
-	acpi_add_ipmi_device(ipmi_device);
-	ipmi_device->user_interface = user;
-	ipmi_device->ipmi_ifnum = iface;
+	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
-	memcpy(&ipmi_device->smi_data, &smi_data, sizeof(struct ipmi_smi_info));
+	put_device(smi_data.dev);
 	return;
 
-out:
+err_lock:
 	mutex_unlock(&driver_data.ipmi_lock);
+	ipmi_dev_release(ipmi_device);
+err_ref:
 	put_device(smi_data.dev);
 	return;
 }
 
 static void ipmi_bmc_gone(int iface)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
 
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev) · line 434

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +3 -7
-		if (ipmi_device->ipmi_ifnum != iface)
-			continue;
-
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
-		break;
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {
+			dev_found = 1;

You can do the list_del() here, because you're under the mutex, so others won't see the list in an inconsistens state and you're about to break anyway.

Quoted 131 lines
drivers/acpi/acpi_ipmi.c +32 -41
+			break;
+		}
 	}
+	if (dev_found)
+		list_del(&ipmi_device->head);
 	mutex_unlock(&driver_data.ipmi_lock);
+
+	if (dev_found) {
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+	}
 }
 
 /* --------------------------------------------------------------------------
@@ -400,7 +467,8 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 			void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
-	struct acpi_ipmi_device *ipmi_device = handler_context;
+	int iface = (long)handler_context;
+	struct acpi_ipmi_device *ipmi_device;
 	int err, rem_time;
 	acpi_status status;
 	unsigned long flags;
@@ -414,12 +482,15 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	if (!ipmi_device->user_interface)
+	ipmi_device = acpi_ipmi_get_targeted_smi(iface);
+	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
 	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg)
-		return AE_NO_MEMORY;
+	if (!tx_msg) {
+		status = AE_NO_MEMORY;
+		goto out_ref;
+	}
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
 		status = AE_TYPE;
@@ -449,6 +520,8 @@ out_list:
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	kfree(tx_msg);
+out_ref:
+	acpi_ipmi_dev_put(ipmi_device);
 	return status;
 }
 
@@ -473,7 +546,7 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 	status = acpi_install_address_space_handler(ipmi->handle,
 						    ACPI_ADR_SPACE_IPMI,
 						    &acpi_ipmi_space_handler,
-						    NULL, ipmi);
+						    NULL, (void *)((long)ipmi->ipmi_ifnum));
 	if (ACPI_FAILURE(status)) {
 		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
@@ -484,36 +557,6 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 	return 0;
 }
 
-static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-
-	INIT_LIST_HEAD(&ipmi_device->head);
-
-	spin_lock_init(&ipmi_device->tx_msg_lock);
-	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
-	ipmi_install_space_handler(ipmi_device);
-
-	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
-}
-
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-	/*
-	 * If the IPMI user interface is created, it should be
-	 * destroyed.
-	 */
-	if (ipmi_device->user_interface) {
-		ipmi_destroy_user(ipmi_device->user_interface);
-		ipmi_device->user_interface = NULL;
-	}
-	/* flush the Tx_msg list */
-	if (!list_empty(&ipmi_device->tx_msg_list))
-		ipmi_flush_tx_msg(ipmi_device);
-
-	list_del(&ipmi_device->head);
-	ipmi_remove_space_handler(ipmi_device);
-}
-
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
@@ -530,7 +573,7 @@ static int __init acpi_ipmi_init(void)
 
 static void __exit acpi_ipmi_exit(void)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	struct acpi_ipmi_device *ipmi_device;
 
 	if (acpi_disabled)
 		return;
@@ -544,11 +587,17 @@ static void __exit acpi_ipmi_exit(void)
 	 * handler and free it.
 	 */
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
+	while (!list_empty(&driver_data.ipmi_devices)) {
+		ipmi_device = list_first_entry(&driver_data.ipmi_devices,
+					       struct acpi_ipmi_device,
+					       head);
+		list_del(&ipmi_device->head);
+		mutex_unlock(&driver_data.ipmi_lock);
+
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+
+		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 }
Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#20 lore raw

RE: [PATCH 04/13] ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user

Quoted 70 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 5:59 AM

Quoted 10 lines · Lv Zheng

This patch uses reference counting to fix the race caused by the unprotected ACPI IPMI user.

As the acpi_ipmi_device->user_interface check in acpi_ipmi_space_handler() can happen before setting user_interface to NULL and codes after the check in acpi_ipmi_space_handler() can happen after user_interface becoming NULL, then the on-going acpi_ipmi_space_handler() still can pass an invalid acpi_ipmi_device->user_interface to ipmi_request_settime(). Such race condition is not allowed by the IPMI layer's API design as crash will happen in

ipmi_request_settime().

Quoted 43 lines

In IPMI layer, smi_gone()/new_smi() callbacks are protected by smi_watchers_mutex, thus their invocations are serialized. But as a new smi can re-use the freed intf_num, it requires that the callback implementation must not use intf_num as an identification mean or it must ensure all references to the previous smi are all dropped before exiting
smi_gone() callback. In case of acpi_ipmi module, this means ipmi_flush_tx_msg() must ensure all on-going IPMI transfers are completed before exiting ipmi_flush_tx_msg().

This patch follows ipmi_devintf.c design:
1. Invoking ipmi_destroy_user() after the reference count of acpi_ipmi_device dropping to 0, this matches IPMI layer's API calling rule on ipmi_destroy_user() and ipmi_request_settime().
2. References of acpi_ipmi_device dropping to 1 means tx_msg related to this acpi_ipmi_device are all freed, this can be used to implement the new flushing mechanism. Note complete() must be retried so that the on-going tx_msg won't block flushing at the point to add tx_msg into tx_msg_list where reference of acpi_ipmi_device is held. This matches the IPMI layer's callback rule on smi_gone()/new_smi() serialization.
3. ipmi_flush_tx_msg() is performed after deleting acpi_ipmi_device from the list so that no new tx_msg can be created after entering flushing process.
4. The flushing of tx_msg is also moved out of ipmi_lock in this patch.

The forthcoming IPMI operation region handler installation changes also requires acpi_ipmi_device be handled in the reference counting style.

Authorship is also updated due to this design change.

---
 drivers/acpi/acpi_ipmi.c |  249
+++++++++++++++++++++++++++-------------------
 1 file changed, 149 insertions(+), 100 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

527ee43..cbf25e0 100644

drivers/acpi/acpi_ipmi.c line 8

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +3 -2
@@ -1,8 +1,9 @@
 /*
  *  acpi_ipmi.c - ACPI IPMI opregion
  *
- *  Copyright (C) 2010 Intel Corporation
- *  Copyright (C) 2010 Zhao Yakui <[email protected]>
+ *  Copyright (C) 2010, 2013 Intel Corporation
+ *    Author: Zhao Yakui <[email protected]>
+ *            Lv Zheng <[email protected]>
  *
  *

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~

Quoted 8 lines

drivers/acpi/acpi_ipmi.c struct acpi_ipmi_device { · line 71

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +1 -0
  *
@@ -67,6 +68,7 @@ struct acpi_ipmi_device {
 	long curr_msgid;
 	unsigned long flags;
 	struct ipmi_smi_info smi_data;
+	atomic_t refcnt;

Can you use a kref instead?

Please see my concerns in another email.

Quoted 91 lines
Quoted 28 lines
drivers/acpi/acpi_ipmi.c +0 -0
 };
 
 struct ipmi_driver_data {
@@ -107,8 +109,8 @@ struct acpi_ipmi_buffer {  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 void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device); -static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device);

drivers/acpi/acpi_ipmi.c +11 -0
+static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
+static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -122,6 +124,80 @@ static struct ipmi_driver_data driver_data = {
 	},
 };
 
+static struct acpi_ipmi_device *
+ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle
+handle) {
+	struct acpi_ipmi_device *ipmi_device;
+	int err;
+	ipmi_user_t user;
+
+	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+	if (!ipmi_device)

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 145

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +10 -0
+		return NULL;
+
+	atomic_set(&ipmi_device->refcnt, 1);
+	INIT_LIST_HEAD(&ipmi_device->head);
+	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
+
+	ipmi_device->handle = handle;
+	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
+	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct

ipmi_smi_info));

Quoted 29 lines
drivers/acpi/acpi_ipmi.c +29 -0
+	ipmi_device->ipmi_ifnum = iface;
+
+	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+			       ipmi_device, &user);
+	if (err) {
+		put_device(smi_data->dev);
+		kfree(ipmi_device);
+		return NULL;
+	}
+	ipmi_device->user_interface = user;
+	ipmi_install_space_handler(ipmi_device);
+
+	return ipmi_device;
+}
+
+static struct acpi_ipmi_device *
+acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device) {
+	if (ipmi_device)
+		atomic_inc(&ipmi_device->refcnt);
+	return ipmi_device;
+}
+
+static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device) {
+	ipmi_remove_space_handler(ipmi_device);
+	ipmi_destroy_user(ipmi_device->user_interface);
+	put_device(ipmi_device->smi_data.dev);
+	kfree(ipmi_device);
+}
+

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 187

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +10 -0
+static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device) {
+	if (ipmi_device && atomic_dec_and_test(&ipmi_device->refcnt))
+		ipmi_dev_release(ipmi_device);
+}
+
+static struct acpi_ipmi_device *acpi_ipmi_get_targeted_smi(int iface)
+{
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
+

Why don't you do

	struct acpi_ipmi_device *ipmi_device, *ret = NULL;

and then ->

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 190

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +3 -0
+	mutex_lock(&driver_data.ipmi_lock);
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {

-> ret = ipmi_device; ->

Quoted 10 lines

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 198

Quoted 8 lines
drivers/acpi/acpi_ipmi.c +8 -0
+			dev_found = 1;
+			acpi_ipmi_dev_get(ipmi_device);
+			break;
+		}
+	}
+	mutex_unlock(&driver_data.ipmi_lock);
+
+	return dev_found ? ipmi_device : NULL;

-> return ret;

OK.

Quoted 28 lines
Quoted 28 lines
drivers/acpi/acpi_ipmi.c +2 -0
+}
+
 static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct
 acpi_ipmi_device *ipmi)  {
 	struct acpi_ipmi_msg *ipmi_msg;
@@ -228,25 +304,24 @@ static void acpi_format_ipmi_response(struct

acpi_ipmi_msg *msg, static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) {

drivers/acpi/acpi_ipmi.c +2 -14
 	struct acpi_ipmi_msg *tx_msg, *temp;
-	int count = HZ / 10;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 	unsigned long flags;
 
-	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-	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);
-	}
-	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
-
-	/* wait for about 100ms to flush the tx message list */
-	while (count--) {
-		if (list_empty(&ipmi->tx_msg_list))
-			break;
-		schedule_timeout(1);
+	/*
+	 * NOTE: Synchronous Flushing
Quoted 8 lines

drivers/acpi/acpi_ipmi.c static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, · line 315

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +5 -0
+	 * Wait until refnct dropping to 1 - no other users unless this
+	 * context.  This function should always be called before
+	 * acpi_ipmi_device destruction.
+	 */
+	while (atomic_read(&ipmi->refcnt) > 1) {

Isn't this racy? What if we see that the refcount is 1 and break the loop, but someone else bumps up the refcount at the same time?

No, it's not racy.
Flushing codes here is invoked after acpi_ipmi_device disappearing from the object managers.
Please look at the ipmi_bmc_gone() and acpi_ipmi_exit().
The ipmi_flush_tx_msg() will only be called after a "list_del()".
There will be no new transfers created in the acpi_ipmi_space_handler() as acpi_ipmi_get_targeted_smi() will return NULL after the "list_del()".

So there are no chances that it reaches to 1 and go back again as the refcount will only increases from 1 to > 1 unless it is still in an object managers. The trick here is to drop all of the object managers' reference and only hold the "call chain" reference here (thus it is 1) in the ipmi_bmc_gone() and acpi_ipmi_exit(). In case of this patch, the object reference count is converted into "call chain" reference count in the ipmi_bmc_gone() and acpi_ipmi_exit(). The waiting codes here then can wait the reference count dropping to 1 which indicates all on-going transfer references are also get dropped.

Quoted 99 lines
Quoted 40 lines
drivers/acpi/acpi_ipmi.c +8 -2
+		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+		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);
+		}
+		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+		schedule_timeout_uninterruptible(msecs_to_jiffies(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) @@ -304,22 +379,26 @@ static void ipmi_register_bmc(int iface, struct device *dev) {

drivers/acpi/acpi_ipmi.c +5 -7
 	struct acpi_ipmi_device *ipmi_device, *temp;
 	struct pnp_dev *pnp_dev;
-	ipmi_user_t		user;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
 
 	err = ipmi_get_smi_info(iface, &smi_data);
-
 	if (err)
 		return;
 
-	if (smi_data.addr_src != SI_ACPI) {
-		put_device(smi_data.dev);
-		return;
-	}
-
+	if (smi_data.addr_src != SI_ACPI)
+		goto err_ref;
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
+	if (!handle)
+		goto err_ref;
+	pnp_dev = to_pnp_dev(smi_data.dev);

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev)

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +6 -1
+
+	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	if (!ipmi_device) {
+		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		goto err_ref;
+	}
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry(temp, &driver_data.ipmi_devices, head) { @@
-328,54 +407,42 @@ static void ipmi_register_bmc(int iface, struct device

*dev)

Quoted 19 lines
drivers/acpi/acpi_ipmi.c +1 -13
 		 * to the device list, don't add it again.
 		 */
 		if (temp->handle == handle)
-			goto out;
+			goto err_lock;
 	}
 
-	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
-
-	if (!ipmi_device)
-		goto out;
-
-	pnp_dev = to_pnp_dev(smi_data.dev);
-	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = pnp_dev;
-
-	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
-					ipmi_device, &user);
-	if (err) {

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev)

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +1 -8
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
-		kfree(ipmi_device);
-		goto out;
-	}
-	acpi_add_ipmi_device(ipmi_device);
-	ipmi_device->user_interface = user;
-	ipmi_device->ipmi_ifnum = iface;
+	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
-	memcpy(&ipmi_device->smi_data, &smi_data, sizeof(struct

ipmi_smi_info));

Quoted 18 lines
drivers/acpi/acpi_ipmi.c +6 -2
+	put_device(smi_data.dev);
 	return;
 
-out:
+err_lock:
 	mutex_unlock(&driver_data.ipmi_lock);
+	ipmi_dev_release(ipmi_device);
+err_ref:
 	put_device(smi_data.dev);
 	return;
 }
 
 static void ipmi_bmc_gone(int iface)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	int dev_found = 0;
+	struct acpi_ipmi_device *ipmi_device;
Quoted 16 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev) · line 434

Quoted 13 lines
drivers/acpi/acpi_ipmi.c +3 -9
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		if (ipmi_device->ipmi_ifnum != iface)
-			continue;
-
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
-		break;
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->ipmi_ifnum == iface) {
+			dev_found = 1;

You can do the list_del() here, because you're under the mutex, so others won't see the list in an inconsistens state and you're about to break anyway.

I'm trying to improve the code maintainability (hence the software internal quality) here for the reviewers.
If we introduce a list_del()/break inside a list_for_each_entry(), then it is pretty much likely that the list_for_each_entry() does not appear in a future patch that deletes the "break". And reviewers could not detect such bug.
The coding style like what I'm showing here can avoid such issue.
I was thinking maintainers would be happy with such codes - it can prevent many unhappy small mistakes from happening.

Thanks for commenting.

Best regards
-Lv

Quoted 137 lines

drivers/acpi/acpi_ipmi.c acpi_ipmi_space_handler(u32 function,

Quoted 27 lines
drivers/acpi/acpi_ipmi.c +9 -2
+			break;
+		}
 	}
+	if (dev_found)
+		list_del(&ipmi_device->head);
 	mutex_unlock(&driver_data.ipmi_lock);
+
+	if (dev_found) {
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+	}
 }
 
 /*
----------------------------------------------------------------------
---- @@ -400,7 +467,8 @@ acpi_ipmi_space_handler(u32 function,

acpi_physical_address address,

drivers/acpi/acpi_ipmi.c +2 -1
 			void *handler_context, void *region_context)  {
 	struct acpi_ipmi_msg *tx_msg;
-	struct acpi_ipmi_device *ipmi_device = handler_context;
+	int iface = (long)handler_context;
+	struct acpi_ipmi_device *ipmi_device;
 	int err, rem_time;
 	acpi_status status;
 	unsigned long flags;
@@ -414,12 +482,15 @@ acpi_ipmi_space_handler(u32 function,

acpi_physical_address address,

drivers/acpi/acpi_ipmi.c static int ipmi_install_space_handler(struct

Quoted 28 lines
drivers/acpi/acpi_ipmi.c +8 -3
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	if (!ipmi_device->user_interface)
+	ipmi_device = acpi_ipmi_get_targeted_smi(iface);
+	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
 	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg)
-		return AE_NO_MEMORY;
+	if (!tx_msg) {
+		status = AE_NO_MEMORY;
+		goto out_ref;
+	}
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
 		status = AE_TYPE;
@@ -449,6 +520,8 @@ out_list:
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	kfree(tx_msg);
+out_ref:
+	acpi_ipmi_dev_put(ipmi_device);
 	return status;
 }
 
@@ -473,7 +546,7 @@ static int ipmi_install_space_handler(struct

acpi_ipmi_device *ipmi)

drivers/acpi/acpi_ipmi.c static int ipmi_install_space_handler(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	status = acpi_install_address_space_handler(ipmi->handle,
 						    ACPI_ADR_SPACE_IPMI,
 						    &acpi_ipmi_space_handler,
-						    NULL, ipmi);
+						    NULL, (void *)((long)ipmi->ipmi_ifnum));
 	if (ACPI_FAILURE(status)) {
 		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
@@ -484,36 +557,6 @@ static int ipmi_install_space_handler(struct

acpi_ipmi_device *ipmi)

Quoted 57 lines
drivers/acpi/acpi_ipmi.c +5 -34
 	return 0;
 }
 
-static void acpi_add_ipmi_device(struct acpi_ipmi_device
 *ipmi_device) -{
-
-	INIT_LIST_HEAD(&ipmi_device->head);
-
-	spin_lock_init(&ipmi_device->tx_msg_lock);
-	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
-	ipmi_install_space_handler(ipmi_device);
-
-	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
-}
-
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device
 *ipmi_device) -{
-	/*
-	 * If the IPMI user interface is created, it should be
-	 * destroyed.
-	 */
-	if (ipmi_device->user_interface) {
-		ipmi_destroy_user(ipmi_device->user_interface);
-		ipmi_device->user_interface = NULL;
-	}
-	/* flush the Tx_msg list */
-	if (!list_empty(&ipmi_device->tx_msg_list))
-		ipmi_flush_tx_msg(ipmi_device);
-
-	list_del(&ipmi_device->head);
-	ipmi_remove_space_handler(ipmi_device);
-}
-
 static int __init acpi_ipmi_init(void)  {
 	int result = 0;
@@ -530,7 +573,7 @@ static int __init acpi_ipmi_init(void)
 
 static void __exit acpi_ipmi_exit(void)  {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	struct acpi_ipmi_device *ipmi_device;
 
 	if (acpi_disabled)
 		return;
@@ -544,11 +587,17 @@ static void __exit acpi_ipmi_exit(void)
 	 * handler and free it.
 	 */
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
+	while (!list_empty(&driver_data.ipmi_devices)) {
+		ipmi_device = list_first_entry(&driver_data.ipmi_devices,
+					       struct acpi_ipmi_device,
+					       head);

drivers/acpi/acpi_ipmi.c static void __exit acpi_ipmi_exit(void) · line 603

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +7 -0
+		list_del(&ipmi_device->head);
+		mutex_unlock(&driver_data.ipmi_lock);
+
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+
+		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 }

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

author
#21 lore raw

PATCH 5/13 ACPI/IPMI: Fix issue caused by the per-device registration of the IPMI operation region handler

In reply to Lv Zheng

It is found on a real machine, in its ACPI namespace, the IPMI OperationRegions (in the ACPI000D - ACPI power meter) are not defined under the IPMI system interface device (the IPI0001 with KCS type returned from _IFT control method):
Device (PMI0)

  {
      Name (_HID, "ACPI000D")  // _HID: Hardware ID
      OperationRegion (SYSI, IPMI, 0x0600, 0x0100)
      Field (SYSI, BufferAcc, Lock, Preserve)
      {
          AccessAs (BufferAcc, 0x01),
          Offset (0x58),
          SCMD,   8,
          GCMD,   8
      }

      OperationRegion (POWR, IPMI, 0x3000, 0x0100)
      Field (POWR, BufferAcc, Lock, Preserve)
      {
          AccessAs (BufferAcc, 0x01),
          Offset (0xB3),
          GPMM,   8
      }

}

Device (PCI0)

  {
      Device (ISA)
      {
          Device (NIPM)
          {
              Name (_HID, EisaId ("IPI0001"))  // _HID: Hardware ID
              Method (_IFT, 0, NotSerialized)  // _IFT: IPMI Interface Type
              {
                  Return (0x01)
              }
          }
      }

}
Current ACPI_IPMI code registers IPMI operation region handler on a per-device basis, so that for above namespace, the IPMI operation region handler is registered only under the scope of \_SB.PCI0.ISA.NIPM. Thus when an IPMI operation region field of \PMI0 is accessed, there are errors reported on such platform:
ACPI Error: No handlers for Region [IPMI]
ACPI Error: Region IPMI(7) has no handler
The solution is to install IPMI operation region handler from root node so that every object that defines IPMI OperationRegion can get an address space handler registered.

When an IPMI operation region field is accessed, the Network Function (0x06 for SYSI and 0x30 for POWR) and the Command (SCMD, GCMD, GPMM) are passed to the operation region handler, there is no system interface specified by the BIOS. The patch tries to select one system interface by monitoring the system interface notification. IPMI messages passed from the ACPI codes are sent to this selected global IPMI system interface.

Known issues:
- How to select the IPMI system interface:
Currently, the ACPI_IPMI always selects the first registered one with the ACPI handle set (i.e., defined in the ACPI namespace). It's hard to determine the selection when there are multiple IPMI system interfaces defined in the ACPI namespace.
According to the IPMI specification:
A BMC device may make available multiple system interfaces, but only one management controller is allowed to be 'active' BMC that provides BMC functionality for the system (in case of a 'partitioned' system, there can be only one active BMC per partition). Only the system interface(s) for the active BMC allowed to respond to the 'Get Device Id' command. According to the ipmi_si desigin:
The ipmi_si registeration notifications can only happen after a successful "Get Device ID" command.
Thus it should be OK for non-partitioned systems to do such selection. But we do not have too much knowledges on 'partitioned' systems.
- Lack of smi_gone()/new_smi() testability:
It is not possible to do module(ipmi_si) load/unload test, and I can't find any multiple IPMI system interfaces platforms available for testing. There might be issues in the untested code path.

---
 drivers/acpi/acpi_ipmi.c |  111 +++++++++++++++++++++++-----------------------
 1 file changed, 55 insertions(+), 56 deletions(-)
drivers/acpi/acpi_ipmi.c +55 -56
index cbf25e0..5f8f495 100644
@@ -46,7 +46,8 @@ MODULE_AUTHOR("Zhao Yakui");
 MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
 MODULE_LICENSE("GPL");
 
-#define IPMI_FLAGS_HANDLER_INSTALL	0
+#undef PREFIX
+#define PREFIX				"ACPI: IPMI: "
 
 #define ACPI_IPMI_OK			0
 #define ACPI_IPMI_TIMEOUT		0x10
@@ -66,7 +67,6 @@ struct acpi_ipmi_device {
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	unsigned long flags;
 	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
@@ -76,6 +76,14 @@ struct ipmi_driver_data {
 	struct ipmi_smi_watcher	bmc_events;
 	struct ipmi_user_hndl	ipmi_hndlrs;
 	struct mutex		ipmi_lock;
+	/*
+	 * NOTE: IPMI System Interface Selection
+	 * There is no system interface specified by the IPMI operation
+	 * region access.  We try to select one system interface with ACPI
+	 * handle set.  IPMI messages passed from the ACPI codes are sent
+	 * to this selected global IPMI system interface.
+	 */
+	struct acpi_ipmi_device *selected_smi;
 };
 
 struct acpi_ipmi_msg {
@@ -109,8 +117,6 @@ struct acpi_ipmi_buffer {
 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 int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
-static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -153,7 +159,6 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 		return NULL;
 	}
 	ipmi_device->user_interface = user;
-	ipmi_install_space_handler(ipmi_device);
 
 	return ipmi_device;
 }
@@ -168,7 +173,6 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device)
 
 static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
 {
-	ipmi_remove_space_handler(ipmi_device);
 	ipmi_destroy_user(ipmi_device->user_interface);
 	put_device(ipmi_device->smi_data.dev);
 	kfree(ipmi_device);
@@ -180,22 +184,15 @@ static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)
 		ipmi_dev_release(ipmi_device);
 }
 
-static struct acpi_ipmi_device *acpi_ipmi_get_targeted_smi(int iface)
+static struct acpi_ipmi_device *acpi_ipmi_get_selected_smi(void)
 {
-	int dev_found = 0;
 	struct acpi_ipmi_device *ipmi_device;
 
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
-		if (ipmi_device->ipmi_ifnum == iface) {
-			dev_found = 1;
-			acpi_ipmi_dev_get(ipmi_device);
-			break;
-		}
-	}
+	ipmi_device = acpi_ipmi_dev_get(driver_data.selected_smi);
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	return dev_found ? ipmi_device : NULL;
+	return ipmi_device;
 }
 
 static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
@@ -410,6 +407,9 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 			goto err_lock;
 	}
 
+	if (!driver_data.selected_smi)
+		driver_data.selected_smi = acpi_ipmi_dev_get(ipmi_device);
+
 	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
 	put_device(smi_data.dev);
@@ -426,22 +426,34 @@ err_ref:
 static void ipmi_bmc_gone(int iface)
 {
 	int dev_found = 0;
-	struct acpi_ipmi_device *ipmi_device;
+	struct acpi_ipmi_device *ipmi_gone, *ipmi_new;
 
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
-		if (ipmi_device->ipmi_ifnum == iface) {
+	list_for_each_entry(ipmi_gone, &driver_data.ipmi_devices, head) {
+		if (ipmi_gone->ipmi_ifnum == iface) {
 			dev_found = 1;
 			break;
 		}
 	}
-	if (dev_found)
-		list_del(&ipmi_device->head);
+	if (dev_found) {
+		list_del(&ipmi_gone->head);
+		if (driver_data.selected_smi == ipmi_gone) {
+			acpi_ipmi_dev_put(ipmi_gone);
+			driver_data.selected_smi = NULL;
+		}
+	}
+	if (!driver_data.selected_smi &&
+	    !list_empty(&driver_data.ipmi_devices)) {
+		ipmi_new = list_first_entry(&driver_data.ipmi_devices,
+					    struct acpi_ipmi_device,
+					    head);
+		driver_data.selected_smi = acpi_ipmi_dev_get(ipmi_new);
+	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
 	if (dev_found) {
-		ipmi_flush_tx_msg(ipmi_device);
-		acpi_ipmi_dev_put(ipmi_device);
+		ipmi_flush_tx_msg(ipmi_gone);
+		acpi_ipmi_dev_put(ipmi_gone);
 	}
 }
 
@@ -467,7 +479,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 			void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
-	int iface = (long)handler_context;
 	struct acpi_ipmi_device *ipmi_device;
 	int err, rem_time;
 	acpi_status status;
@@ -482,7 +493,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_get_targeted_smi(iface);
+	ipmi_device = acpi_ipmi_get_selected_smi();
 	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
@@ -525,48 +536,28 @@ out_ref:
 	return status;
 }
 
-static void ipmi_remove_space_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_space_handler(struct acpi_ipmi_device *ipmi)
+static int __init acpi_ipmi_init(void)
 {
+	int result = 0;
 	acpi_status status;
 
-	if (test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags))
-		return 0;
+	if (acpi_disabled)
+		return result;
 
-	status = acpi_install_address_space_handler(ipmi->handle,
+	mutex_init(&driver_data.ipmi_lock);
+
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
 						    ACPI_ADR_SPACE_IPMI,
 						    &acpi_ipmi_space_handler,
-						    NULL, (void *)((long)ipmi->ipmi_ifnum));
+						    NULL, NULL);
 	if (ACPI_FAILURE(status)) {
-		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
-		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
-			"handle\n");
+		pr_warn("Can't register IPMI opregion space handle\n");
 		return -EINVAL;
 	}
-	set_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
-	return 0;
-}
-
-static int __init acpi_ipmi_init(void)
-{
-	int result = 0;
-
-	if (acpi_disabled)
-		return result;
-
-	mutex_init(&driver_data.ipmi_lock);
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
+	if (result)
+		pr_err("Can't register IPMI system interface watcher\n");
 
 	return result;
 }
@@ -592,6 +583,10 @@ static void __exit acpi_ipmi_exit(void)
 					       struct acpi_ipmi_device,
 					       head);
 		list_del(&ipmi_device->head);
+		if (ipmi_device == driver_data.selected_smi) {
+			acpi_ipmi_dev_put(driver_data.selected_smi);
+			driver_data.selected_smi = NULL;
+		}
 		mutex_unlock(&driver_data.ipmi_lock);
 
 		ipmi_flush_tx_msg(ipmi_device);
@@ -600,6 +595,10 @@ static void __exit acpi_ipmi_exit(void)
 		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
+
+	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
+					  ACPI_ADR_SPACE_IPMI,
+					  &acpi_ipmi_space_handler);
 }
 
 module_init(acpi_ipmi_init);
Signature · 1 line
1.7.10
author
#22 lore raw

PATCH 6/13 ACPI/IPMI: Add reference counting for ACPI operation region handlers

In reply to Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe environment. The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224 ++++++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c +6 -10
index 5f8f495..2a09156 100644
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c +224 -0

233 lines not on this page · raw message

include/acpi/acpi_bus.h +5 -0
index a2c2fbb..15fad0d 100644
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void *bus) { return 0; }
 
 #endif				/* CONFIG_ACPI */
 
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context);
+void acpi_unregister_region(acpi_adr_space_type space_id);
+
 #endif /*__ACPI_BUS_H__*/
Signature · 1 line
1.7.10
#23 lore raw

Re: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 73 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe environment. The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224 ++++++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c +6 -10
index 5f8f495..2a09156 100644
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c +0 -0
index 6ab2c35..8398e51 100644

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 95

Quoted 10 lines
drivers/acpi/osl.c +7 -0
@@ -86,6 +86,42 @@ static struct workqueue_struct *kacpid_wq;
 static struct workqueue_struct *kacpi_notify_wq;
 static struct workqueue_struct *kacpi_hotplug_wq;
 
+struct acpi_region {
+	unsigned long flags;
+#define ACPI_REGION_DEFAULT		0x01
+#define ACPI_REGION_INSTALLED		0x02
+#define ACPI_REGION_REGISTERED		0x04
+#define ACPI_REGION_UNREGISTERING	0x08
+#define ACPI_REGION_INSTALLING		0x10

What about (1UL << 1), (1UL << 2) etc.?

Also please remove the #defines out of the struct definition.

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 106

Quoted 11 lines
drivers/acpi/osl.c +11 -0
+	/*
+	 * NOTE: Upgrading All Region Handlers
+	 * This flag is only used during the period where not all of the
+	 * region handers are upgraded to the new interfaces.
+	 */
+#define ACPI_REGION_MANAGED		0x80
+	acpi_adr_space_handler handler;
+	acpi_adr_space_setup setup;
+	void *context;
+	/* Invoking references */
+	atomic_t refcnt;

Actually, why don't you use krefs?

Quoted 59 lines
drivers/acpi/osl.c +52 -0
+};
+
+static struct acpi_region acpi_regions[ACPI_NUM_PREDEFINED_REGIONS] = {
+	[ACPI_ADR_SPACE_SYSTEM_MEMORY] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_SYSTEM_IO] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_PCI_CONFIG] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_IPMI] = {
+		.flags = ACPI_REGION_MANAGED,
+	},
+};
+static DEFINE_MUTEX(acpi_mutex_region);
+
 /*
  * This list of permanent mappings is for memory that may be accessed from
  * interrupt context, where we can't do the ioremap().
@@ -1799,3 +1835,191 @@ void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,
 		kfree(hp_work);
 }
 EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+
+static bool acpi_region_managed(struct acpi_region *rgn)
+{
+	/*
+	 * NOTE: Default and Managed
+	 * We only need to avoid region management on the regions managed
+	 * by ACPICA (ACPI_REGION_DEFAULT).  Currently, we need additional
+	 * check as many operation region handlers are not upgraded, so
+	 * only those known to be safe are managed (ACPI_REGION_MANAGED).
+	 */
+	return !(rgn->flags & ACPI_REGION_DEFAULT) &&
+	       (rgn->flags & ACPI_REGION_MANAGED);
+}
+
+static bool acpi_region_callable(struct acpi_region *rgn)
+{
+	return (rgn->flags & ACPI_REGION_REGISTERED) &&
+	       !(rgn->flags & ACPI_REGION_UNREGISTERING);
+}
+
+static acpi_status
+acpi_region_default_handler(u32 function,
+			    acpi_physical_address address,
+			    u32 bit_width, u64 *value,
+			    void *handler_context, void *region_context)
+{
+	acpi_adr_space_handler handler;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_NOT_EXIST;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
+		mutex_unlock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1881

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = handler(function, address, bit_width, value, context,
+			 region_context);

Why don't we call the handler under the mutex?

What exactly prevents context from becoming NULL before the call above?

Quoted 16 lines
drivers/acpi/osl.c +16 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static acpi_status
+acpi_region_default_setup(acpi_handle handle, u32 function,
+			  void *handler_context, void **region_context)
+{
+	acpi_adr_space_setup setup;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_OK;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->setup) {

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1907

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		mutex_unlock(&acpi_mutex_region);
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	setup = rgn->setup;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = setup(handle, function, context, region_context);

Can setup drop rgn->refcnt ?

Quoted 89 lines
drivers/acpi/osl.c +89 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static int __acpi_install_region(struct acpi_region *rgn,
+				 acpi_adr_space_type space_id)
+{
+	int res = 0;
+	acpi_status status;
+	int installing = 0;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_INSTALLED)
+		goto out_lock;
+	if (rgn->flags & ACPI_REGION_INSTALLING) {
+		res = -EBUSY;
+		goto out_lock;
+	}
+
+	installing = 1;
+	rgn->flags |= ACPI_REGION_INSTALLING;
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, space_id,
+						    acpi_region_default_handler,
+						    acpi_region_default_setup,
+						    rgn);
+	rgn->flags &= ~ACPI_REGION_INSTALLING;
+	if (ACPI_FAILURE(status))
+		res = -EINVAL;
+	else
+		rgn->flags |= ACPI_REGION_INSTALLED;
+
+out_lock:
+	mutex_unlock(&acpi_mutex_region);
+	if (installing) {
+		if (res)
+			pr_err("Failed to install region %d\n", space_id);
+		else
+			pr_info("Region %d installed\n", space_id);
+	}
+	return res;
+}
+
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context)
+{
+	int res;
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return -EINVAL;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))
+		return -EINVAL;
+
+	res = __acpi_install_region(rgn, space_id);
+	if (res)
+		return res;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_REGISTERED) {
+		mutex_unlock(&acpi_mutex_region);
+		return -EBUSY;
+	}
+
+	rgn->handler = handler;
+	rgn->setup = setup;
+	rgn->context = context;
+	rgn->flags |= ACPI_REGION_REGISTERED;
+	atomic_set(&rgn->refcnt, 1);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d registered\n", space_id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_region);
+
+void acpi_unregister_region(acpi_adr_space_type space_id)
+{
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2006

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!(rgn->flags & ACPI_REGION_REGISTERED)) {
+		mutex_unlock(&acpi_mutex_region);
+		return;
+	}
+	if (rgn->flags & ACPI_REGION_UNREGISTERING) {
+		mutex_unlock(&acpi_mutex_region);
+		return;

What about

	if ((rgn->flags & ACPI_REGION_UNREGISTERING)
	    || !(rgn->flags & ACPI_REGION_REGISTERED)) {
		mutex_unlock(&acpi_mutex_region);
		return;
	}

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2016

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	}
+
+	rgn->flags |= ACPI_REGION_UNREGISTERING;
+	rgn->handler = NULL;
+	rgn->setup = NULL;
+	rgn->context = NULL;
+	mutex_unlock(&acpi_mutex_region);
+
+	while (atomic_read(&rgn->refcnt) > 1)
+		schedule_timeout_uninterruptible(usecs_to_jiffies(5));

Wouldn't it be better to use a wait queue here?

Quoted 14 lines
drivers/acpi/osl.c +9 -0
+	atomic_dec(&rgn->refcnt);
+
+	mutex_lock(&acpi_mutex_region);
+	rgn->flags &= ~(ACPI_REGION_REGISTERED | ACPI_REGION_UNREGISTERING);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d unregistered\n", space_id);
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_region);
include/acpi/acpi_bus.h +0 -0
index a2c2fbb..15fad0d 100644

include/acpi/acpi_bus.h static inline int unregister_acpi_bus_type(void *bus) { return 0; } · line 550

Quoted 9 lines
include/acpi/acpi_bus.h +5 -0
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void *bus) { return 0; }
 
 #endif				/* CONFIG_ACPI */
 
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context);
+void acpi_unregister_region(acpi_adr_space_type space_id);
+
 #endif /*__ACPI_BUS_H__*/

Thanks,
Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#24 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 93 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 4:27 AM

Quoted 6 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe

environment.

Quoted 13 lines

The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224

++++++++++++++++++++++++++++++++++++++++++++++

Quoted 51 lines
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

5f8f495..2a09156 100644

drivers/acpi/acpi_ipmi.c +6 -10
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)  {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c index renamed from drivers/acpi/osl.c +0 -0

6ab2c35..8398e51 100644

drivers/acpi/osl.c +0 -0
@@ -86,6 +86,42 @@ static struct workqueue_struct *kacpid_wq;  static

struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq;

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 95

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+struct acpi_region {
+	unsigned long flags;
+#define ACPI_REGION_DEFAULT		0x01
+#define ACPI_REGION_INSTALLED		0x02
+#define ACPI_REGION_REGISTERED		0x04
+#define ACPI_REGION_UNREGISTERING	0x08
+#define ACPI_REGION_INSTALLING		0x10

What about (1UL << 1), (1UL << 2) etc.?

Quoted 1 line

Also please remove the #defines out of the struct definition.

OK.

Quoted 14 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 106

Quoted 12 lines
drivers/acpi/osl.c +11 -0
+	/*
+	 * NOTE: Upgrading All Region Handlers
+	 * This flag is only used during the period where not all of the
+	 * region handers are upgraded to the new interfaces.
+	 */
+#define ACPI_REGION_MANAGED		0x80
+	acpi_adr_space_handler handler;
+	acpi_adr_space_setup setup;
+	void *context;
+	/* Invoking references */
+	atomic_t refcnt;

Actually, why don't you use krefs?

If you take a look at other piece of my codes, you'll find there are two reasons:

1. I'm using while (atomic_read() > 1) to implement the objects' flushing and there is no kref API to do so.
I just think it is not suitable for me to introduce such an API into kref.h and start another argument around kref designs in this bug fix patch. :-) I'll start a discussion about kref design using another thread.
2. I'm using ipmi_dev|msg_release() as a pair of ipmi_dev|msg_alloc(), it's kind of atomic_t coding style.
If atomic_t is changed to struct kref, I will need to implement two API, __ipmi_dev_release() to take a struct kref as parameter and call ipmi_dev_release inside it. By not using kref, I needn't write codes to implement such API.

Quoted 71 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 4 lines
drivers/acpi/osl.c +3 -0
+};
+
+static struct acpi_region acpi_regions[ACPI_NUM_PREDEFINED_REGIONS]

= {

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+	[ACPI_ADR_SPACE_SYSTEM_MEMORY] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_SYSTEM_IO] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_PCI_CONFIG] = {

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 10 lines
drivers/acpi/osl.c +8 -0
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_IPMI] = {
+		.flags = ACPI_REGION_MANAGED,
+	},
+};
+static DEFINE_MUTEX(acpi_mutex_region);
+
 /*
  * This list of permanent mappings is for memory that may be accessed

from

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle,

Quoted 2 lines

* interrupt context, where we can't do the ioremap().

drivers/acpi/osl.c +0 -0
@@ -1799,3 +1835,191 @@ void alloc_acpi_hp_work(acpi_handle handle,

u32 type, void *context,

Quoted 32 lines
drivers/acpi/osl.c +29 -0
 		kfree(hp_work);
 }
 EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+
+static bool acpi_region_managed(struct acpi_region *rgn) {
+	/*
+	 * NOTE: Default and Managed
+	 * We only need to avoid region management on the regions managed
+	 * by ACPICA (ACPI_REGION_DEFAULT).  Currently, we need additional
+	 * check as many operation region handlers are not upgraded, so
+	 * only those known to be safe are managed (ACPI_REGION_MANAGED).
+	 */
+	return !(rgn->flags & ACPI_REGION_DEFAULT) &&
+	       (rgn->flags & ACPI_REGION_MANAGED); }
+
+static bool acpi_region_callable(struct acpi_region *rgn) {
+	return (rgn->flags & ACPI_REGION_REGISTERED) &&
+	       !(rgn->flags & ACPI_REGION_UNREGISTERING); }
+
+static acpi_status
+acpi_region_default_handler(u32 function,
+			    acpi_physical_address address,
+			    u32 bit_width, u64 *value,
+			    void *handler_context, void *region_context) {
+	acpi_adr_space_handler handler;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_NOT_EXIST;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
+		mutex_unlock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1881

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = handler(function, address, bit_width, value, context,
+			 region_context);

Why don't we call the handler under the mutex?

Quoted 1 line

What exactly prevents context from becoming NULL before the call above?

It's a kind of programming style related concern.
IMO, using locks around callback function is a buggy programming style that could lead to dead locks. Let me explain this using an example.

Object A exports a register/unregister API for other objects.
Object B calls A's register/unregister API to register/unregister B's callback.
It's likely that object B will hold lock_of_B around unregister/register when object B is destroyed/created, the lock_of_B is likely also used inside the callback. So when object A holds the lock_of_A around the callback invocation, it leads to dead lock since:
1. the locking order for the register/unregister side will be: lock(lock_of_B), lock(lock_of_A)
2. the locking order for the callback side will be: lock(lock_of_A), lock(lock_of_B)
They are in the reversed order!

IMO, Linux may need to introduce __callback, __api as decelerators for the functions, and use sparse to enforce this rule, sparse knows if a callback is invoked under some locks.

In the case of ACPICA space_handlers, as you may know, when an ACPI operation region handler is invoked, there will be no lock held inside ACPICA (interpreter lock must be freed before executing operation region handlers). So the likelihood of the dead lock is pretty much high here!

Quoted 20 lines
Quoted 20 lines
drivers/acpi/osl.c +19 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static acpi_status
+acpi_region_default_setup(acpi_handle handle, u32 function,
+			  void *handler_context, void **region_context) {
+	acpi_adr_space_setup setup;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_OK;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->setup) {
+		mutex_unlock(&acpi_mutex_region);
+		return status;
+	}
+
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1907

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	atomic_inc(&rgn->refcnt);
+	setup = rgn->setup;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = setup(handle, function, context, region_context);

Can setup drop rgn->refcnt ?

The reason is same as the handler, as a setup is also a callback.

Quoted 102 lines
Quoted 14 lines
drivers/acpi/osl.c +13 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static int __acpi_install_region(struct acpi_region *rgn,
+				 acpi_adr_space_type space_id)
+{
+	int res = 0;
+	acpi_status status;
+	int installing = 0;
+
+	mutex_lock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	if (rgn->flags & ACPI_REGION_INSTALLED)
+		goto out_lock;
+	if (rgn->flags & ACPI_REGION_INSTALLING) {
+		res = -EBUSY;
+		goto out_lock;
+	}
+
+	installing = 1;
+	rgn->flags |= ACPI_REGION_INSTALLING;
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,

space_id,

Quoted 64 lines
drivers/acpi/osl.c +64 -0
+						    acpi_region_default_handler,
+						    acpi_region_default_setup,
+						    rgn);
+	rgn->flags &= ~ACPI_REGION_INSTALLING;
+	if (ACPI_FAILURE(status))
+		res = -EINVAL;
+	else
+		rgn->flags |= ACPI_REGION_INSTALLED;
+
+out_lock:
+	mutex_unlock(&acpi_mutex_region);
+	if (installing) {
+		if (res)
+			pr_err("Failed to install region %d\n", space_id);
+		else
+			pr_info("Region %d installed\n", space_id);
+	}
+	return res;
+}
+
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context) {
+	int res;
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return -EINVAL;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))
+		return -EINVAL;
+
+	res = __acpi_install_region(rgn, space_id);
+	if (res)
+		return res;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_REGISTERED) {
+		mutex_unlock(&acpi_mutex_region);
+		return -EBUSY;
+	}
+
+	rgn->handler = handler;
+	rgn->setup = setup;
+	rgn->context = context;
+	rgn->flags |= ACPI_REGION_REGISTERED;
+	atomic_set(&rgn->refcnt, 1);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d registered\n", space_id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_region);
+
+void acpi_unregister_region(acpi_adr_space_type space_id) {
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2006

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!(rgn->flags & ACPI_REGION_REGISTERED)) {
+		mutex_unlock(&acpi_mutex_region);
+		return;
+	}
+	if (rgn->flags & ACPI_REGION_UNREGISTERING) {
+		mutex_unlock(&acpi_mutex_region);
+		return;

What about

Quoted 6 lines
	if ((rgn->flags & ACPI_REGION_UNREGISTERING)
	    || !(rgn->flags & ACPI_REGION_REGISTERED)) {
		mutex_unlock(&acpi_mutex_region);
		return;
	}

OK.

Quoted 4 lines
Quoted 4 lines
drivers/acpi/osl.c +4 -0
+	}
+
+	rgn->flags |= ACPI_REGION_UNREGISTERING;
+	rgn->handler = NULL;
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2016

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	rgn->setup = NULL;
+	rgn->context = NULL;
+	mutex_unlock(&acpi_mutex_region);
+
+	while (atomic_read(&rgn->refcnt) > 1)
+		schedule_timeout_uninterruptible(usecs_to_jiffies(5));

Wouldn't it be better to use a wait queue here?

Yes, I'll try.

Quoted 19 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 5 lines
drivers/acpi/osl.c +4 -0
+	atomic_dec(&rgn->refcnt);
+
+	mutex_lock(&acpi_mutex_region);
+	rgn->flags &= ~(ACPI_REGION_REGISTERED |

ACPI_REGION_UNREGISTERING);

Quoted 13 lines
drivers/acpi/osl.c +4 -0
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d unregistered\n", space_id); }
+EXPORT_SYMBOL_GPL(acpi_unregister_region);
include/acpi/acpi_bus.h index renamed from include/acpi/acpi_bus.h +0 -0

a2c2fbb..15fad0d 100644

include/acpi/acpi_bus.h +0 -0
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void
 *bus) { return 0; }
 
 #endif				/* CONFIG_ACPI */
 
Quoted 9 lines
Quoted 6 lines
include/acpi/acpi_bus.h +5 -0
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context); void
+acpi_unregister_region(acpi_adr_space_type space_id);
+

#endif /*__ACPI_BUS_H__*/

Thanks,
Rafael

Thanks
-Lv

Quoted 5 lines

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

author
#25 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 168 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Zheng, Lv
Sent: Friday, July 26, 2013 8:48 AM

Quoted 92 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 4:27 AM

Quoted 6 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe

environment.

Quoted 13 lines

The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224

++++++++++++++++++++++++++++++++++++++++++++++

Quoted 52 lines
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c +0 -0

index
5f8f495..2a09156 100644

drivers/acpi/acpi_ipmi.c +6 -10
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)  {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c index renamed from drivers/acpi/osl.c +0 -0

6ab2c35..8398e51 100644

drivers/acpi/osl.c +0 -0
@@ -86,6 +86,42 @@ static struct workqueue_struct *kacpid_wq;

static struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq;

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 95

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+struct acpi_region {
+	unsigned long flags;
+#define ACPI_REGION_DEFAULT		0x01
+#define ACPI_REGION_INSTALLED		0x02
+#define ACPI_REGION_REGISTERED		0x04
+#define ACPI_REGION_UNREGISTERING	0x08
+#define ACPI_REGION_INSTALLING		0x10

What about (1UL << 1), (1UL << 2) etc.?

Quoted 1 line

Also please remove the #defines out of the struct definition.

OK.

Quoted 14 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 106

Quoted 12 lines
drivers/acpi/osl.c +11 -0
+	/*
+	 * NOTE: Upgrading All Region Handlers
+	 * This flag is only used during the period where not all of the
+	 * region handers are upgraded to the new interfaces.
+	 */
+#define ACPI_REGION_MANAGED		0x80
+	acpi_adr_space_handler handler;
+	acpi_adr_space_setup setup;
+	void *context;
+	/* Invoking references */
+	atomic_t refcnt;

Actually, why don't you use krefs?

If you take a look at other piece of my codes, you'll find there are two reasons:

1. I'm using while (atomic_read() > 1) to implement the objects' flushing and there is no kref API to do so.
I just think it is not suitable for me to introduce such an API into kref.h and
start another argument around kref designs in this bug fix patch. :-)
I'll start a discussion about kref design using another thread.
2. I'm using ipmi_dev|msg_release() as a pair of ipmi_dev|msg_alloc(), it's kind of atomic_t coding style.
If atomic_t is changed to struct kref, I will need to implement two API,
__ipmi_dev_release() to take a struct kref as parameter and call ipmi_dev_release inside it.
By not using kref, I needn't write codes to implement such API.

Quoted 4 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 4 lines
drivers/acpi/osl.c +3 -0
+};
+
+static struct acpi_region

acpi_regions[ACPI_NUM_PREDEFINED_REGIONS]

Quoted 23 lines

= {

Quoted 17 lines
drivers/acpi/osl.c +15 -0
+	[ACPI_ADR_SPACE_SYSTEM_MEMORY] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_SYSTEM_IO] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_PCI_CONFIG] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_IPMI] = {
+		.flags = ACPI_REGION_MANAGED,
+	},
+};
+static DEFINE_MUTEX(acpi_mutex_region);
+
 /*
  * This list of permanent mappings is for memory that may be
Quoted 1 line

accessed

from

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle,

Quoted 2 lines

* interrupt context, where we can't do the ioremap().

drivers/acpi/osl.c +0 -0
@@ -1799,3 +1835,191 @@ void alloc_acpi_hp_work(acpi_handle handle,

u32 type, void *context,

Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 8 lines
drivers/acpi/osl.c +5 -0
 		kfree(hp_work);
 }
 EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+
+static bool acpi_region_managed(struct acpi_region *rgn) {
+	/*
+	 * NOTE: Default and Managed
+	 * We only need to avoid region management on the regions

managed

Quoted 38 lines
Quoted 1 line

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, · line 1843

Quoted 1 line
drivers/acpi/osl.c +1 -0
+	 * by ACPICA (ACPI_REGION_DEFAULT).  Currently, we need

additional

Quoted 2 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, · line 1845

Quoted 2 lines
drivers/acpi/osl.c +2 -0
+	 * check as many operation region handlers are not upgraded, so
+	 * only those known to be safe are managed

(ACPI_REGION_MANAGED).

Quoted 33 lines
Quoted 21 lines
drivers/acpi/osl.c +21 -0
+	 */
+	return !(rgn->flags & ACPI_REGION_DEFAULT) &&
+	       (rgn->flags & ACPI_REGION_MANAGED); }
+
+static bool acpi_region_callable(struct acpi_region *rgn) {
+	return (rgn->flags & ACPI_REGION_REGISTERED) &&
+	       !(rgn->flags & ACPI_REGION_UNREGISTERING); }
+
+static acpi_status
+acpi_region_default_handler(u32 function,
+			    acpi_physical_address address,
+			    u32 bit_width, u64 *value,
+			    void *handler_context, void *region_context) {
+	acpi_adr_space_handler handler;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_NOT_EXIST;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
+		mutex_unlock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1881

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = handler(function, address, bit_width, value, context,
+			 region_context);

Why don't we call the handler under the mutex?

I think my reply is against this question, let me remove it up.

Quoted 7 lines

It's a kind of programming style related concern.
IMO, using locks around callback function is a buggy programming style that could lead to dead locks.
Let me explain this using an example.

Object A exports a register/unregister API for other objects. Object B calls A's register/unregister API to register/unregister B's callback.

Sorry I have to use object rather than module here as there might be several objects inside a module that have the same situation need to be handled.

Quoted 8 lines

It's likely that object B will hold lock_of_B around unregister/register when object B is destroyed/created, the lock_of_B is likely also used inside the callback.
So when object A holds the lock_of_A around the callback invocation, it leads to dead lock since:
1. the locking order for the register/unregister side will be: lock(lock_of_B), lock(lock_of_A) 2. the locking order for the callback side will be: lock(lock_of_A), lock(lock_of_B) They are in the reversed order!

I think this example is not quite correct.
There is another aspect in unregister implementation which is the intent of this patch:
No callback running/imitated after "unregister", we can call this a "flush" requirement. Inside of the callback, lock_of_B should not be held if "flush" is required.

Quoted 9 lines

IMO, Linux may need to introduce __callback, __api as decelerators for the functions, and use sparse to enforce this rule, sparse knows if a callback is invoked under some locks.

In the case of ACPICA space_handlers, as you may know, when an ACPI operation region handler is invoked, there will be no lock held inside ACPICA (interpreter lock must be freed before executing operation region handlers). So the likelihood of the dead lock is pretty much high here!

I need to mention another requirement of the operation region handler.
It is required that multiple operation region handlers are executed at the same time, or the IO operations invoked by the BIOS ASL codes will be serialized. IMO, IO operations invoked by the BIOS ASL need to be parallelized.
Thus mutex is not useful to implement a protection here.

So the mutex is unlocked before executing the handler, IMO, reference counting is useful here to meet this requirement.

Quoted 2 lines
Quoted 2 lines

What exactly prevents context from becoming NULL before the call above?

I think my answers did not answer this question directly.

Sorry that I'm not clear what you want to ask here. Let me just try to be practical.

The code is here:

Quoted 7 lines
Quoted 7 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1878

Quoted 7 lines
drivers/acpi/osl.c +5 -0
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
 
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);

The handler is ensured not to be NULL within the mutex lock.

Thanks for commenting.

Best regards
-Lv

Quoted 196 lines
Quoted 20 lines
Quoted 20 lines
drivers/acpi/osl.c +19 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static acpi_status
+acpi_region_default_setup(acpi_handle handle, u32 function,
+			  void *handler_context, void **region_context) {
+	acpi_adr_space_setup setup;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_OK;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->setup) {
+		mutex_unlock(&acpi_mutex_region);
+		return status;
+	}
+
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1907

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	atomic_inc(&rgn->refcnt);
+	setup = rgn->setup;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = setup(handle, function, context, region_context);

Can setup drop rgn->refcnt ?

The reason is same as the handler, as a setup is also a callback.

Quoted 102 lines
Quoted 14 lines
drivers/acpi/osl.c +13 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static int __acpi_install_region(struct acpi_region *rgn,
+				 acpi_adr_space_type space_id)
+{
+	int res = 0;
+	acpi_status status;
+	int installing = 0;
+
+	mutex_lock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	if (rgn->flags & ACPI_REGION_INSTALLED)
+		goto out_lock;
+	if (rgn->flags & ACPI_REGION_INSTALLING) {
+		res = -EBUSY;
+		goto out_lock;
+	}
+
+	installing = 1;
+	rgn->flags |= ACPI_REGION_INSTALLING;
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,

space_id,

Quoted 64 lines
drivers/acpi/osl.c +64 -0
+						    acpi_region_default_handler,
+						    acpi_region_default_setup,
+						    rgn);
+	rgn->flags &= ~ACPI_REGION_INSTALLING;
+	if (ACPI_FAILURE(status))
+		res = -EINVAL;
+	else
+		rgn->flags |= ACPI_REGION_INSTALLED;
+
+out_lock:
+	mutex_unlock(&acpi_mutex_region);
+	if (installing) {
+		if (res)
+			pr_err("Failed to install region %d\n", space_id);
+		else
+			pr_info("Region %d installed\n", space_id);
+	}
+	return res;
+}
+
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context) {
+	int res;
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return -EINVAL;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))
+		return -EINVAL;
+
+	res = __acpi_install_region(rgn, space_id);
+	if (res)
+		return res;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_REGISTERED) {
+		mutex_unlock(&acpi_mutex_region);
+		return -EBUSY;
+	}
+
+	rgn->handler = handler;
+	rgn->setup = setup;
+	rgn->context = context;
+	rgn->flags |= ACPI_REGION_REGISTERED;
+	atomic_set(&rgn->refcnt, 1);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d registered\n", space_id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_region);
+
+void acpi_unregister_region(acpi_adr_space_type space_id) {
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2006

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!(rgn->flags & ACPI_REGION_REGISTERED)) {
+		mutex_unlock(&acpi_mutex_region);
+		return;
+	}
+	if (rgn->flags & ACPI_REGION_UNREGISTERING) {
+		mutex_unlock(&acpi_mutex_region);
+		return;

What about

Quoted 6 lines
	if ((rgn->flags & ACPI_REGION_UNREGISTERING)
	    || !(rgn->flags & ACPI_REGION_REGISTERED)) {
		mutex_unlock(&acpi_mutex_region);
		return;
	}

OK.

Quoted 4 lines
Quoted 4 lines
drivers/acpi/osl.c +4 -0
+	}
+
+	rgn->flags |= ACPI_REGION_UNREGISTERING;
+	rgn->handler = NULL;
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2016

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	rgn->setup = NULL;
+	rgn->context = NULL;
+	mutex_unlock(&acpi_mutex_region);
+
+	while (atomic_read(&rgn->refcnt) > 1)
+		schedule_timeout_uninterruptible(usecs_to_jiffies(5));

Wouldn't it be better to use a wait queue here?

Yes, I'll try.

Quoted 19 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 5 lines
drivers/acpi/osl.c +4 -0
+	atomic_dec(&rgn->refcnt);
+
+	mutex_lock(&acpi_mutex_region);
+	rgn->flags &= ~(ACPI_REGION_REGISTERED |

ACPI_REGION_UNREGISTERING);

Quoted 13 lines
drivers/acpi/osl.c +4 -0
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d unregistered\n", space_id); }
+EXPORT_SYMBOL_GPL(acpi_unregister_region);
include/acpi/acpi_bus.h index renamed from include/acpi/acpi_bus.h +0 -0

a2c2fbb..15fad0d 100644

include/acpi/acpi_bus.h +0 -0
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void
 *bus) { return 0; }
 
 #endif				/* CONFIG_ACPI */
 
Quoted 9 lines
Quoted 6 lines
include/acpi/acpi_bus.h +5 -0
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context); void
+acpi_unregister_region(acpi_adr_space_type space_id);
+

#endif /*__ACPI_BUS_H__*/

Thanks,
Rafael

Thanks
-Lv

Quoted 5 lines

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

N r y b X ǧv ^ )޺{.n + { i b {ay ʇڙ ,j f h z  w
j:+v w j m zZ+ ݢj" ! i

#26 lore raw

Re: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

In reply to Zheng, Lv
Quoted 111 lines · Lv
Quoted 92 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 4:27 AM

Quoted 6 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe

environment.

Quoted 13 lines

The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224

++++++++++++++++++++++++++++++++++++++++++++++

Quoted 51 lines
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

5f8f495..2a09156 100644

drivers/acpi/acpi_ipmi.c +6 -10
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)  {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c index renamed from drivers/acpi/osl.c +0 -0

6ab2c35..8398e51 100644

drivers/acpi/osl.c +0 -0
@@ -86,6 +86,42 @@ static struct workqueue_struct *kacpid_wq;  static

struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq;

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 95

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+struct acpi_region {
+	unsigned long flags;
+#define ACPI_REGION_DEFAULT		0x01
+#define ACPI_REGION_INSTALLED		0x02
+#define ACPI_REGION_REGISTERED		0x04
+#define ACPI_REGION_UNREGISTERING	0x08
+#define ACPI_REGION_INSTALLING		0x10

What about (1UL << 1), (1UL << 2) etc.?

Quoted 1 line

Also please remove the #defines out of the struct definition.

OK.

Quoted 14 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 106

Quoted 12 lines
drivers/acpi/osl.c +11 -0
+	/*
+	 * NOTE: Upgrading All Region Handlers
+	 * This flag is only used during the period where not all of the
+	 * region handers are upgraded to the new interfaces.
+	 */
+#define ACPI_REGION_MANAGED		0x80
+	acpi_adr_space_handler handler;
+	acpi_adr_space_setup setup;
+	void *context;
+	/* Invoking references */
+	atomic_t refcnt;

Actually, why don't you use krefs?

Quoted 3 lines

If you take a look at other piece of my codes, you'll find there are two reasons:

1. I'm using while (atomic_read() > 1) to implement the objects' flushing and there is no kref API to do so.

No, there's not any, but you can read kref.refcount directly, can't you?

Moreover, it is not entirely clear to me that doing the while (atomic_read() > 1) is actually correct.

Quoted 2 lines

I just think it is not suitable for me to introduce such an API into kref.h and start another argument around kref designs in this bug fix patch. :-) I'll start a discussion about kref design using another thread.

You don't need to do that at all.

Quoted 3 lines

2. I'm using ipmi_dev|msg_release() as a pair of ipmi_dev|msg_alloc(), it's kind of atomic_t coding style.
If atomic_t is changed to struct kref, I will need to implement two API, __ipmi_dev_release() to take a struct kref as parameter and call ipmi_dev_release inside it. By not using kref, I needn't write codes to implement such API.

I'm not following you, sorry.

Please just use krefs for reference counting, the same way as you use struct list_head for implementing lists. This is the way everyone does that in the kernel and that's for a reason.

Unless you do your reference counting under a lock, in which case using atomic_t isn't necessary at all and you can use a non-atomic counter.

Quoted 72 lines
Quoted 70 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 3 lines
drivers/acpi/osl.c +3 -0
+};
+
+static struct acpi_region acpi_regions[ACPI_NUM_PREDEFINED_REGIONS]

= {

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+	[ACPI_ADR_SPACE_SYSTEM_MEMORY] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_SYSTEM_IO] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_PCI_CONFIG] = {

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 10 lines
drivers/acpi/osl.c +8 -0
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_IPMI] = {
+		.flags = ACPI_REGION_MANAGED,
+	},
+};
+static DEFINE_MUTEX(acpi_mutex_region);
+
 /*
  * This list of permanent mappings is for memory that may be accessed

from

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle,

Quoted 2 lines

* interrupt context, where we can't do the ioremap().

drivers/acpi/osl.c +0 -0
@@ -1799,3 +1835,191 @@ void alloc_acpi_hp_work(acpi_handle handle,

u32 type, void *context,

Quoted 32 lines
drivers/acpi/osl.c +29 -0
 		kfree(hp_work);
 }
 EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+
+static bool acpi_region_managed(struct acpi_region *rgn) {
+	/*
+	 * NOTE: Default and Managed
+	 * We only need to avoid region management on the regions managed
+	 * by ACPICA (ACPI_REGION_DEFAULT).  Currently, we need additional
+	 * check as many operation region handlers are not upgraded, so
+	 * only those known to be safe are managed (ACPI_REGION_MANAGED).
+	 */
+	return !(rgn->flags & ACPI_REGION_DEFAULT) &&
+	       (rgn->flags & ACPI_REGION_MANAGED); }
+
+static bool acpi_region_callable(struct acpi_region *rgn) {
+	return (rgn->flags & ACPI_REGION_REGISTERED) &&
+	       !(rgn->flags & ACPI_REGION_UNREGISTERING); }
+
+static acpi_status
+acpi_region_default_handler(u32 function,
+			    acpi_physical_address address,
+			    u32 bit_width, u64 *value,
+			    void *handler_context, void *region_context) {
+	acpi_adr_space_handler handler;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_NOT_EXIST;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
+		mutex_unlock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1881

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = handler(function, address, bit_width, value, context,
+			 region_context);

Why don't we call the handler under the mutex?

Quoted 1 line

What exactly prevents context from becoming NULL before the call above?

Quoted 7 lines

It's a kind of programming style related concern.
IMO, using locks around callback function is a buggy programming style that could lead to dead locks. Let me explain this using an example.

Object A exports a register/unregister API for other objects.
Object B calls A's register/unregister API to register/unregister B's callback.
It's likely that object B will hold lock_of_B around unregister/register when object B is destroyed/created, the lock_of_B is likely also used inside the callback.

Why is it likely to be used inside the callback? Clearly, if a callback is executed under a lock, that lock can't be acquired by that callback.

Quoted 6 lines

So when object A holds the lock_of_A around the callback invocation, it leads to dead lock since:
1. the locking order for the register/unregister side will be: lock(lock_of_B), lock(lock_of_A)
2. the locking order for the callback side will be: lock(lock_of_A), lock(lock_of_B) They are in the reversed order!

IMO, Linux may need to introduce __callback, __api as decelerators for the functions, and use sparse to enforce this rule, sparse knows if a callback is invoked under some locks.

Oh, dear. Yes, sparse knows such things, and so what?

Quoted 2 lines

In the case of ACPICA space_handlers, as you may know, when an ACPI operation region handler is invoked, there will be no lock held inside ACPICA (interpreter lock must be freed before executing operation region handlers). So the likelihood of the dead lock is pretty much high here!

Sorry, what are you talking about?

Please let me rephrase my question: What *practical* problems would it lead to if we executed this particular callback under this particular mutex?

Not *theoretical* in the general theory of everything, *practical* in this particular piece of code.

And we are talking about a *global* mutex here, not something object-specific.

Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
drivers/acpi/osl.c +19 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static acpi_status
+acpi_region_default_setup(acpi_handle handle, u32 function,
+			  void *handler_context, void **region_context) {
+	acpi_adr_space_setup setup;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_OK;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->setup) {
+		mutex_unlock(&acpi_mutex_region);
+		return status;
+	}
+
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1907

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	atomic_inc(&rgn->refcnt);
+	setup = rgn->setup;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = setup(handle, function, context, region_context);

Can setup drop rgn->refcnt ?

Quoted 1 line

The reason is same as the handler, as a setup is also a callback.

Let me rephrase: Is it legitimate for setup to modify rgn->refcnt? If so, then why?

Quoted 124 lines
Quoted 102 lines
Quoted 14 lines
drivers/acpi/osl.c +13 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static int __acpi_install_region(struct acpi_region *rgn,
+				 acpi_adr_space_type space_id)
+{
+	int res = 0;
+	acpi_status status;
+	int installing = 0;
+
+	mutex_lock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	if (rgn->flags & ACPI_REGION_INSTALLED)
+		goto out_lock;
+	if (rgn->flags & ACPI_REGION_INSTALLING) {
+		res = -EBUSY;
+		goto out_lock;
+	}
+
+	installing = 1;
+	rgn->flags |= ACPI_REGION_INSTALLING;
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,

space_id,

Quoted 64 lines
drivers/acpi/osl.c +64 -0
+						    acpi_region_default_handler,
+						    acpi_region_default_setup,
+						    rgn);
+	rgn->flags &= ~ACPI_REGION_INSTALLING;
+	if (ACPI_FAILURE(status))
+		res = -EINVAL;
+	else
+		rgn->flags |= ACPI_REGION_INSTALLED;
+
+out_lock:
+	mutex_unlock(&acpi_mutex_region);
+	if (installing) {
+		if (res)
+			pr_err("Failed to install region %d\n", space_id);
+		else
+			pr_info("Region %d installed\n", space_id);
+	}
+	return res;
+}
+
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context) {
+	int res;
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return -EINVAL;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))
+		return -EINVAL;
+
+	res = __acpi_install_region(rgn, space_id);
+	if (res)
+		return res;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_REGISTERED) {
+		mutex_unlock(&acpi_mutex_region);
+		return -EBUSY;
+	}
+
+	rgn->handler = handler;
+	rgn->setup = setup;
+	rgn->context = context;
+	rgn->flags |= ACPI_REGION_REGISTERED;
+	atomic_set(&rgn->refcnt, 1);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d registered\n", space_id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_region);
+
+void acpi_unregister_region(acpi_adr_space_type space_id) {
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2006

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!(rgn->flags & ACPI_REGION_REGISTERED)) {
+		mutex_unlock(&acpi_mutex_region);
+		return;
+	}
+	if (rgn->flags & ACPI_REGION_UNREGISTERING) {
+		mutex_unlock(&acpi_mutex_region);
+		return;

What about

Quoted 6 lines
	if ((rgn->flags & ACPI_REGION_UNREGISTERING)
	    || !(rgn->flags & ACPI_REGION_REGISTERED)) {
		mutex_unlock(&acpi_mutex_region);
		return;
	}

OK.

Quoted 4 lines
Quoted 4 lines
drivers/acpi/osl.c +4 -0
+	}
+
+	rgn->flags |= ACPI_REGION_UNREGISTERING;
+	rgn->handler = NULL;
Quoted 8 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2016

Quoted 6 lines
drivers/acpi/osl.c +6 -0
+	rgn->setup = NULL;
+	rgn->context = NULL;
+	mutex_unlock(&acpi_mutex_region);
+
+	while (atomic_read(&rgn->refcnt) > 1)
+		schedule_timeout_uninterruptible(usecs_to_jiffies(5));

Wouldn't it be better to use a wait queue here?

Quoted 1 line

Yes, I'll try.

By the way, we do we need to do that?

Quoted 16 lines
Quoted 16 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 4 lines
drivers/acpi/osl.c +4 -0
+	atomic_dec(&rgn->refcnt);
+
+	mutex_lock(&acpi_mutex_region);
+	rgn->flags &= ~(ACPI_REGION_REGISTERED |

ACPI_REGION_UNREGISTERING);

Quoted 11 lines
drivers/acpi/osl.c +4 -0
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d unregistered\n", space_id); }
+EXPORT_SYMBOL_GPL(acpi_unregister_region);
include/acpi/acpi_bus.h index renamed from include/acpi/acpi_bus.h +0 -0

a2c2fbb..15fad0d 100644

include/acpi/acpi_bus.h +0 -0
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void
 *bus) { return 0; }
 
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/acpi/acpi_bus.h +5 -0
 #endif				/* CONFIG_ACPI */
 
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context); void
+acpi_unregister_region(acpi_adr_space_type space_id);
+

#endif /*__ACPI_BUS_H__*/

Thanks,
Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#27 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 137 lines · Rafael J. Wysocki
Quoted 73 lines · Lv
Quoted 72 lines · Rafael J. Wysocki
Quoted 6 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment. This patch tries to protect the address space callbacks by invoking them under a module safe

environment.

Quoted 13 lines

The IPMI address space handler is also upgraded in this patch. The acpi_unregister_region() is designed to meet the following requirements:
1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.
Using reference counting rather than module referencing allows such benefits to be achieved even when acpi_unregister_region() is called in the environments other than module->exit().
The header file of include/acpi/acpi_bus.h should contain the declarations that have references to some ACPICA defined types.

Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |   16 ++--
 drivers/acpi/osl.c       |  224

++++++++++++++++++++++++++++++++++++++++++++++

Quoted 42 lines
 include/acpi/acpi_bus.h  |    5 ++
 3 files changed, 235 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

5f8f495..2a09156 100644

drivers/acpi/acpi_ipmi.c +6 -10
@@ -539,20 +539,18 @@ out_ref:
 static int __init acpi_ipmi_init(void)  {
 	int result = 0;
-	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
-	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, NULL);
-	if (ACPI_FAILURE(status)) {
+	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
+				      &acpi_ipmi_space_handler,
+				      NULL, NULL);
+	if (result) {
 		pr_warn("Can't register IPMI opregion space handle\n");
-		return -EINVAL;
+		return result;
 	}
 
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
@@ -596,9 +594,7 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
-	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-					  ACPI_ADR_SPACE_IPMI,
-					  &acpi_ipmi_space_handler);
+	acpi_unregister_region(ACPI_ADR_SPACE_IPMI);
 }
 
Quoted 6 lines
Quoted 6 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +0 -0
 module_init(acpi_ipmi_init);
drivers/acpi/osl.c index renamed from drivers/acpi/osl.c +0 -0

6ab2c35..8398e51 100644

drivers/acpi/osl.c +0 -0
@@ -86,6 +86,42 @@ static struct workqueue_struct *kacpid_wq;

static

Quoted 32 lines
Quoted 13 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 95

Quoted 10 lines

struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq;

drivers/acpi/osl.c +7 -0
+struct acpi_region {
+	unsigned long flags;
+#define ACPI_REGION_DEFAULT		0x01
+#define ACPI_REGION_INSTALLED		0x02
+#define ACPI_REGION_REGISTERED		0x04
+#define ACPI_REGION_UNREGISTERING	0x08
+#define ACPI_REGION_INSTALLING		0x10

What about (1UL << 1), (1UL << 2) etc.?

Quoted 1 line

Also please remove the #defines out of the struct definition.

OK.

Quoted 14 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq; · line 106

Quoted 12 lines
drivers/acpi/osl.c +11 -0
+	/*
+	 * NOTE: Upgrading All Region Handlers
+	 * This flag is only used during the period where not all of the
+	 * region handers are upgraded to the new interfaces.
+	 */
+#define ACPI_REGION_MANAGED		0x80
+	acpi_adr_space_handler handler;
+	acpi_adr_space_setup setup;
+	void *context;
+	/* Invoking references */
+	atomic_t refcnt;

Actually, why don't you use krefs?

Quoted 1 line

If you take a look at other piece of my codes, you'll find there are two

reasons:

Quoted 2 lines

1. I'm using while (atomic_read() > 1) to implement the objects' flushing and

there is no kref API to do so.

No, there's not any, but you can read kref.refcount directly, can't you?

Moreover, it is not entirely clear to me that doing the while (atomic_read() > 1) is actually correct.

Quoted 1 line

I just think it is not suitable for me to introduce such an API into kref.h and

start another argument around kref designs in this bug fix patch. :-)

Quoted 1 line

I'll start a discussion about kref design using another thread.

You don't need to do that at all.

Quoted 1 line

2. I'm using ipmi_dev|msg_release() as a pair of ipmi_dev|msg_alloc(), it's

kind of atomic_t coding style.

Quoted 1 line

If atomic_t is changed to struct kref, I will need to implement two API,

__ipmi_dev_release() to take a struct kref as parameter and call ipmi_dev_release inside it.

Quoted 1 line

By not using kref, I needn't write codes to implement such API.

Quoted 8 lines

I'm not following you, sorry.

Please just use krefs for reference counting, the same way as you use struct list_head for implementing lists. This is the way everyone does that in the kernel and that's for a reason.

Unless you do your reference counting under a lock, in which case using atomic_t isn't necessary at all and you can use a non-atomic counter.

I'll follow your suggestion of kref.
You can find my concern 2 related stuff in the next revision. It's trivial.

Quoted 83 lines
Quoted 4 lines
Quoted 4 lines

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 4 lines
drivers/acpi/osl.c +3 -0
+};
+
+static struct acpi_region

acpi_regions[ACPI_NUM_PREDEFINED_REGIONS]

Quoted 19 lines
Quoted 19 lines

= {

Quoted 7 lines
drivers/acpi/osl.c +7 -0
+	[ACPI_ADR_SPACE_SYSTEM_MEMORY] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_SYSTEM_IO] = {
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_PCI_CONFIG] = {

drivers/acpi/osl.c static struct workqueue_struct *kacpid_wq;

Quoted 10 lines
drivers/acpi/osl.c +8 -0
+		.flags = ACPI_REGION_DEFAULT,
+	},
+	[ACPI_ADR_SPACE_IPMI] = {
+		.flags = ACPI_REGION_MANAGED,
+	},
+};
+static DEFINE_MUTEX(acpi_mutex_region);
+
 /*
  * This list of permanent mappings is for memory that may be accessed

from

Quoted 2 lines
Quoted 2 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle

Quoted 2 lines

* interrupt context, where we can't do the ioremap().

drivers/acpi/osl.c +0 -0
@@ -1799,3 +1835,191 @@ void alloc_acpi_hp_work(acpi_handle

handle,

Quoted 9 lines
Quoted 9 lines

u32 type, void *context,

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 8 lines
drivers/acpi/osl.c +5 -0
 		kfree(hp_work);
 }
 EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+
+static bool acpi_region_managed(struct acpi_region *rgn) {
+	/*
+	 * NOTE: Default and Managed
+	 * We only need to avoid region management on the regions

managed

Quoted 1 line
Quoted 1 line

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle · line 1843

Quoted 1 line
drivers/acpi/osl.c +1 -0
+	 * by ACPICA (ACPI_REGION_DEFAULT).  Currently, we need

additional

Quoted 2 lines
Quoted 2 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle · line 1845

Quoted 2 lines
drivers/acpi/osl.c +2 -0
+	 * check as many operation region handlers are not upgraded, so
+	 * only those known to be safe are managed

(ACPI_REGION_MANAGED).

Quoted 34 lines
Quoted 34 lines
Quoted 21 lines
drivers/acpi/osl.c +21 -0
+	 */
+	return !(rgn->flags & ACPI_REGION_DEFAULT) &&
+	       (rgn->flags & ACPI_REGION_MANAGED); }
+
+static bool acpi_region_callable(struct acpi_region *rgn) {
+	return (rgn->flags & ACPI_REGION_REGISTERED) &&
+	       !(rgn->flags & ACPI_REGION_UNREGISTERING); }
+
+static acpi_status
+acpi_region_default_handler(u32 function,
+			    acpi_physical_address address,
+			    u32 bit_width, u64 *value,
+			    void *handler_context, void *region_context) {
+	acpi_adr_space_handler handler;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_NOT_EXIST;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->handler) {
+		mutex_unlock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1881

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	handler = rgn->handler;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = handler(function, address, bit_width, value, context,
+			 region_context);

Why don't we call the handler under the mutex?

Quoted 4 lines
Quoted 1 line

What exactly prevents context from becoming NULL before the call above?

It's a kind of programming style related concern.
IMO, using locks around callback function is a buggy programming style that

could lead to dead locks.

Quoted 2 lines

Let me explain this using an example.

Quoted 8 lines
Quoted 3 lines

Object A exports a register/unregister API for other objects. Object B calls A's register/unregister API to register/unregister B's callback. It's likely that object B will hold lock_of_B around unregister/register when

object B is destroyed/created, the lock_of_B is likely also used inside the callback.

Why is it likely to be used inside the callback? Clearly, if a callback is executed under a lock, that lock can't be acquired by that callback.

I think this is not related to the real purpose of why we must not hold a lock in this situation. So let's ignore this paragraph.

Quoted 6 lines
Quoted 2 lines

So when object A holds the lock_of_A around the callback invocation, it leads

to dead lock since:

Quoted 1 line

1. the locking order for the register/unregister side will be: lock(lock_of_B),

lock(lock_of_A)

Quoted 1 line

2. the locking order for the callback side will be: lock(lock_of_A),

Quoted 8 lines

lock(lock_of_B)

Quoted 3 lines

They are in the reversed order!

IMO, Linux may need to introduce __callback, __api as decelerators for the

functions, and use sparse to enforce this rule, sparse knows if a callback is invoked under some locks.

Oh, dear. Yes, sparse knows such things, and so what?

I was thinking sparse can give us warnings on __api marked function invocation where __acquire count is not 0, this might be mandatory for high quality codes. And sparse can also give us warnings on __callback marked function invocations where __acquire count is not 0, this should be optional. But since it is not related to our topic, let's ignore this paragraph.

Quoted 8 lines
Quoted 2 lines

In the case of ACPICA space_handlers, as you may know, when an ACPI

operation region handler is invoked, there will be no lock held inside ACPICA (interpreter lock must be freed before executing operation region handlers).

Quoted 1 line

So the likelihood of the dead lock is pretty much high here!

Sorry, what are you talking about?

Quoted 8 lines

Please let me rephrase my question: What *practical* problems would it lead to
if we executed this particular callback under this particular mutex?

Not *theoretical* in the general theory of everything, *practical* in this particular piece of code.

And we are talking about a *global* mutex here, not something object-specific.

I think you have additional replies on this in another email. Let me reply you there.

Quoted 29 lines
Quoted 29 lines
Quoted 28 lines
Quoted 16 lines
drivers/acpi/osl.c +15 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static acpi_status
+acpi_region_default_setup(acpi_handle handle, u32 function,
+			  void *handler_context, void **region_context) {
+	acpi_adr_space_setup setup;
+	struct acpi_region *rgn = (struct acpi_region *)handler_context;
+	void *context;
+	acpi_status status = AE_OK;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!acpi_region_callable(rgn) || !rgn->setup) {

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 1907

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		mutex_unlock(&acpi_mutex_region);
+		return status;
+	}
+
+	atomic_inc(&rgn->refcnt);
+	setup = rgn->setup;
+	context = rgn->context;
+	mutex_unlock(&acpi_mutex_region);
+
+	status = setup(handle, function, context, region_context);

Can setup drop rgn->refcnt ?

Quoted 4 lines
Quoted 1 line

The reason is same as the handler, as a setup is also a callback.

Let me rephrase: Is it legitimate for setup to modify rgn->refcnt? If so, then why?

Yes, the race is same as the handler.
When ACPICA is accessing the text segment of the setup function implementation, the module owns the setup function can also be unloaded as there is no lock hold before invoking setup - note that ExitInter also happens to setup invocations.

Quoted 125 lines
Quoted 125 lines
Quoted 103 lines
Quoted 15 lines
drivers/acpi/osl.c +13 -0
+	atomic_dec(&rgn->refcnt);
+
+	return status;
+}
+
+static int __acpi_install_region(struct acpi_region *rgn,
+				 acpi_adr_space_type space_id)
+{
+	int res = 0;
+	acpi_status status;
+	int installing = 0;
+
+	mutex_lock(&acpi_mutex_region);

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	if (rgn->flags & ACPI_REGION_INSTALLED)
+		goto out_lock;
+	if (rgn->flags & ACPI_REGION_INSTALLING) {
+		res = -EBUSY;
+		goto out_lock;
+	}
+
+	installing = 1;
+	rgn->flags |= ACPI_REGION_INSTALLING;
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,

space_id,

Quoted 64 lines
drivers/acpi/osl.c +64 -0
+						    acpi_region_default_handler,
+						    acpi_region_default_setup,
+						    rgn);
+	rgn->flags &= ~ACPI_REGION_INSTALLING;
+	if (ACPI_FAILURE(status))
+		res = -EINVAL;
+	else
+		rgn->flags |= ACPI_REGION_INSTALLED;
+
+out_lock:
+	mutex_unlock(&acpi_mutex_region);
+	if (installing) {
+		if (res)
+			pr_err("Failed to install region %d\n", space_id);
+		else
+			pr_info("Region %d installed\n", space_id);
+	}
+	return res;
+}
+
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context) {
+	int res;
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return -EINVAL;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))
+		return -EINVAL;
+
+	res = __acpi_install_region(rgn, space_id);
+	if (res)
+		return res;
+
+	mutex_lock(&acpi_mutex_region);
+	if (rgn->flags & ACPI_REGION_REGISTERED) {
+		mutex_unlock(&acpi_mutex_region);
+		return -EBUSY;
+	}
+
+	rgn->handler = handler;
+	rgn->setup = setup;
+	rgn->context = context;
+	rgn->flags |= ACPI_REGION_REGISTERED;
+	atomic_set(&rgn->refcnt, 1);
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d registered\n", space_id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_region);
+
+void acpi_unregister_region(acpi_adr_space_type space_id) {
+	struct acpi_region *rgn;
+
+	if (space_id >= ACPI_NUM_PREDEFINED_REGIONS)
+		return;
+
+	rgn = &acpi_regions[space_id];
+	if (!acpi_region_managed(rgn))

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2006

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+		return;
+
+	mutex_lock(&acpi_mutex_region);
+	if (!(rgn->flags & ACPI_REGION_REGISTERED)) {
+		mutex_unlock(&acpi_mutex_region);
+		return;
+	}
+	if (rgn->flags & ACPI_REGION_UNREGISTERING) {
+		mutex_unlock(&acpi_mutex_region);
+		return;

What about

Quoted 6 lines
	if ((rgn->flags & ACPI_REGION_UNREGISTERING)
	    || !(rgn->flags & ACPI_REGION_REGISTERED)) {
		mutex_unlock(&acpi_mutex_region);
		return;
	}

OK.

Quoted 12 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, · line 2016

Quoted 10 lines
drivers/acpi/osl.c +10 -0
+	}
+
+	rgn->flags |= ACPI_REGION_UNREGISTERING;
+	rgn->handler = NULL;
+	rgn->setup = NULL;
+	rgn->context = NULL;
+	mutex_unlock(&acpi_mutex_region);
+
+	while (atomic_read(&rgn->refcnt) > 1)
+		schedule_timeout_uninterruptible(usecs_to_jiffies(5));

Wouldn't it be better to use a wait queue here?

Quoted 3 lines
Quoted 1 line

Yes, I'll try.

By the way, we do we need to do that?

I think you have additional replies on this in another email. Let me reply you there.

Thanks for commenting.

Best regards
-Lv

Quoted 33 lines
Quoted 17 lines
Quoted 17 lines

drivers/acpi/osl.c void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,

Quoted 5 lines
drivers/acpi/osl.c +4 -0
+	atomic_dec(&rgn->refcnt);
+
+	mutex_lock(&acpi_mutex_region);
+	rgn->flags &= ~(ACPI_REGION_REGISTERED |

ACPI_REGION_UNREGISTERING);

Quoted 11 lines
drivers/acpi/osl.c +4 -0
+	mutex_unlock(&acpi_mutex_region);
+
+	pr_info("Region %d unregistered\n", space_id); }
+EXPORT_SYMBOL_GPL(acpi_unregister_region);
include/acpi/acpi_bus.h index renamed from include/acpi/acpi_bus.h +0 -0

a2c2fbb..15fad0d 100644

include/acpi/acpi_bus.h +0 -0
@@ -542,4 +542,9 @@ static inline int unregister_acpi_bus_type(void
 *bus) { return 0; }
 
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/acpi/acpi_bus.h +5 -0
 #endif				/* CONFIG_ACPI */
 
+int acpi_register_region(acpi_adr_space_type space_id,
+			 acpi_adr_space_handler handler,
+			 acpi_adr_space_setup setup, void *context); void
+acpi_unregister_region(acpi_adr_space_type space_id);
+

#endif /*__ACPI_BUS_H__*/

Thanks,
Rafael

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

#28 lore raw

Re: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

In reply to Lv Zheng
Quoted 5 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment.

Actually, can you please explain to me what *exactly* the problem is?

Rafael

author
#29 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 11 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 5:29 AM

Quoted 5 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment.

Actually, can you please explain to me what *exactly* the problem is?

OK. I'll add race explanations in the next revision.

The problem is there is no "lock" held inside ACPICA for invoking operation region handlers. Thus races happens between the acpi_remove/install_address_space_handler and the handler/setup callbacks.

This is correct per ACPI specification.
As if there is interpreter locks held for invoking operation region handlers, the timeout implemented inside the operation region handlers will make all locking facilities (Acquire or Sleep,...) timed out.
Please refer to ACPI specification "5.5.2 Control Method Execution":
Interpretation of a Control Method is not preemptive, but it can block. When a control method does block, OSPM can initiate or continue the execution of a different control method. A control method can only assume that access to global objects is exclusive for any period the control method does not block.

So it is pretty much likely that ACPI IO transfers are locked inside the operation region callback implementations. Using locking facility to protect the callback invocation will risk dead locks.

Thanks
-Lv

Quoted 1 line

Rafael

author
#30 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 19 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Zheng, Lv
Sent: Friday, July 26, 2013 9:54 AM
To: Rafael J. Wysocki
Cc: Wysocki, Rafael J; Brown, Len; [email protected]; [email protected]
Subject: RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 10 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 5:29 AM

Quoted 5 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment.

Quoted 8 lines
Quoted 1 line

Actually, can you please explain to me what *exactly* the problem is?

OK. I'll add race explanations in the next revision.

The problem is there is no "lock" held inside ACPICA for invoking operation region handlers.
Thus races happens between the acpi_remove/install_address_space_handler and the handler/setup callbacks.

This seems not a good explanation of the intent of this patch. I think the intent is here in the patch description:

1. It acts as a barrier for operation region callbacks - no callback will happen after acpi_unregister_region().
2. acpi_unregister_region() is safe to be called in moudle->exit() functions.

Hmm, maybe I need to re-order the patch description for this patch.

Thanks for commenting.

Best regards
-Lv

Quoted 19 lines

This is correct per ACPI specification.
As if there is interpreter locks held for invoking operation region handlers, the timeout implemented inside the operation region handlers will make all locking facilities (Acquire or Sleep,...) timed out.
Please refer to ACPI specification "5.5.2 Control Method Execution":
Interpretation of a Control Method is not preemptive, but it can block. When a control method does block, OSPM can initiate or continue the execution of a different control method. A control method can only assume that access to global objects is exclusive for any period the control method does not block.

So it is pretty much likely that ACPI IO transfers are locked inside the operation region callback implementations.
Using locking facility to protect the callback invocation will risk dead locks.

Thanks
-Lv

Quoted 1 line

Rafael

#31 lore raw

Re: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

In reply to Zheng, Lv
Quoted 10 lines · Lv
Quoted 10 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 5:29 AM

Quoted 5 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback invocations.

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment.

Quoted 8 lines
Quoted 1 line

Actually, can you please explain to me what *exactly* the problem is?

OK. I'll add race explanations in the next revision.

The problem is there is no "lock" held inside ACPICA for invoking operation region handlers.
Thus races happens between the acpi_remove/install_address_space_handler and the handler/setup callbacks.

I see. Now you're trying to introduce something that would prevent those races from happening, right?

Quoted 10 lines

This is correct per ACPI specification.
As if there is interpreter locks held for invoking operation region handlers, the timeout implemented inside the operation region handlers will make all locking facilities (Acquire or Sleep,...) timed out.
Please refer to ACPI specification "5.5.2 Control Method Execution":
Interpretation of a Control Method is not preemptive, but it can block. When a control method does block, OSPM can initiate or continue the execution of a different control method. A control method can only assume that access to global objects is exclusive for any period the control method does not block.

Quoted 3 lines

So it is pretty much likely that ACPI IO transfers are locked inside the operation region callback implementations.
Using locking facility to protect the callback invocation will risk dead locks.

No. If you use a single global lock around all invocations of operation region handlers, it won't deadlock, but it will *serialize* things. This means that there won't be two handlers executing in parallel. That may or may not be bad depending on what those handlers actually do.

Your concern seems to be that if one address space handler is buggy and it blocks indefinitely, executing it under such a lock would affect the other address space handlers and in my opinion this is a valid concern.

So the idea seems to be to add wrappers around acpi_install_address_space_handler() and acpi_remove_address_space_handler (but I don't see where the latter is called after the change?), such that they will know when it is safe to unregister the handler. That is simple enough.

However, I'm not sure it is needed in the context of IPMI. Your address space handler's context is NULL, so even it if is executed after acpi_remove_address_space_handler() has been called for it (or in parallel), it doesn't depend on anything passed by the caller, so I don't see why the issue can't be addressed by a proper synchronization between acpi_ipmi_exit() and acpi_ipmi_space_handler().

Clearly, acpi_ipmi_exit() should wait for all already running instances of acpi_ipmi_space_handler() to complete and all acpi_ipmi_space_handler() instances started after acpi_ipmi_exit() has been called must return immediately.

I would imagine an algorithm like this:

acpi_ipmi_exit()
1. Take "address space handler lock".
2. Set "unregistering address space handler" flag.
3. Check if "count of currently running handlers" is 0. If so,
call acpi_remove_address_space_handler(), drop the lock (possibly clear the flag) and return.
4. Otherwise drop the lock and go to sleep in "address space handler wait queue".
5. When woken up, take "address space handler lock" and go to 3.

acpi_ipmi_space_handler()
1. Take "address space handler lock".
2. Check "unregistering address space handler" flag. If set, drop the lock and return.
3. Increment "count of currently running handlers".
4. Drop the lock.
5. Do your work.
6. Take "address space handler lock".
7. Decrement "count of currently running handlers" and if 0, signal the tasks waiting on it to wake up.
8. Drop the lock.

Thanks,
Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#32 lore raw

RE: [PATCH 06/13] ACPI/IPMI: Add reference counting for ACPI operation region handlers

Quoted 15 lines · Rafael J. Wysocki
Quoted 5 lines · Lv
Quoted 4 lines · Rafael J. Wysocki
Quoted 2 lines · Lv Zheng

This patch adds reference couting for ACPI operation region handlers to fix races caused by the ACPICA address space callback

invocations.

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

ACPICA address space callback invocation is not suitable for Linux CONFIG_MODULE=y execution environment.

Actually, can you please explain to me what *exactly* the problem is?

OK. I'll add race explanations in the next revision.

Quoted 8 lines
Quoted 4 lines

The problem is there is no "lock" held inside ACPICA for invoking operation region handlers.
Thus races happens between the acpi_remove/install_address_space_handler and the handler/setup

callbacks.

I see. Now you're trying to introduce something that would prevent those races from happening, right?

Yes. Let me explain this later in this email.

Quoted 16 lines
Quoted 10 lines

This is correct per ACPI specification.
As if there is interpreter locks held for invoking operation region handlers, the timeout implemented inside the operation region handlers will make all locking facilities (Acquire or Sleep,...) timed out. Please refer to ACPI specification "5.5.2 Control Method Execution":
Interpretation of a Control Method is not preemptive, but it can block. When a control method does block, OSPM can initiate or continue the execution of a different control method. A control method can only assume that access to global objects is exclusive for any period the control

method does not block.

Quoted 4 lines

So it is pretty much likely that ACPI IO transfers are locked inside the operation region callback implementations.
Using locking facility to protect the callback invocation will risk dead locks.

Quoted 8 lines

No. If you use a single global lock around all invocations of operation region handlers, it won't deadlock, but it will *serialize* things. This means that there won't be two handlers executing in parallel. That may or may not be bad depending on what those handlers actually do.

Your concern seems to be that if one address space handler is buggy and it blocks indefinitely, executing it under such a lock would affect the other address space handlers and in my opinion this is a valid concern.

It can be expressed in more detailed ways:

The interpreter runs control methods in the following style according to the ACPI spec.
CM1_Enter -> EnterInter -> CM1_Running -> OpRegion1 -> ExitInter -> EnterInter -> CM1_running -> ExitInter -> CM1_Exit
CM2_Enter -> EnterInter -> -> CM2_Running -> OpRegion1 -> ExitInter -> EnterInter -> CM2_running -> ExitInter -> CM2_Exit

EnterInter: Enter interpreter lock
ExitInter: Leave interpreter lock

Let me introduce two situations:

1. If we hold global "mutex" before "EnterInter", then no second control method can be run "NotSerialized".
If the CM1 just have some codes waiting for a hardware flag and CM2 can access other hardware IOs to trigger this flag, then nothing can happen any longer.
This is a practical bug as what we have already seen in "NotSerialized" marked ACPI control methods behave in the interpreter mode executed in serialized way - kernel parameter "acpi_serialize".

2. If we hold global "mutex" after "EnterInter" and Before OpRegion1
If we do things this way, then all IO accesses are serialized, if we have something in an IPMI operation region failed due to timeout, then any other system IOs that should happen in parallel will just happen after 5 seconds. This is not an acceptable experience.

Quoted 6 lines

So the idea seems to be to add wrappers around acpi_install_address_space_handler()
and acpi_remove_address_space_handler (but I don't see where the latter is called after the change?), such that they will know when it is safe to unregister the handler. That is simple enough.

An obvious bug, it should be put between the while (atomic_read() > 1) block and the final atomic_dec().

Quoted 1 line

However, I'm not sure it is needed in the context of IPMI.

I think I do this just because I need a quick fix to test IPMI bug-fix series.
The issue is highly related to ACPI interpreter design, and codes should be implemented inside ACPICA.
And there is not only ACPI_ROOT_OBJECT based address space handlers, but also non-ACPI_ROOT_OBJECT based address space handlers, this patch can't protect the latter ones.

Quoted 25 lines

Your address space
handler's context is NULL, so even it if is executed after acpi_remove_address_space_handler() has been called for it (or in parallel), it doesn't depend on anything passed by the caller, so I don't see why the issue can't be addressed by a proper synchronization between acpi_ipmi_exit() and acpi_ipmi_space_handler().

Clearly, acpi_ipmi_exit() should wait for all already running instances of acpi_ipmi_space_handler() to complete and all acpi_ipmi_space_handler() instances started after acpi_ipmi_exit() has been called must return immediately.

I would imagine an algorithm like this:

acpi_ipmi_exit()
1. Take "address space handler lock".
2. Set "unregistering address space handler" flag.
3. Check if "count of currently running handlers" is 0. If so, call acpi_remove_address_space_handler(), drop the lock (possibly clear
the
flag) and return.
4. Otherwise drop the lock and go to sleep in "address space handler wait
queue".
5. When woken up, take "address space handler lock" and go to 3.

Quoted 11 lines

acpi_ipmi_space_handler()
1. Take "address space handler lock".
2. Check "unregistering address space handler" flag. If set, drop the lock and return.
3. Increment "count of currently running handlers".
4. Drop the lock.
5. Do your work.
6. Take "address space handler lock".
7. Decrement "count of currently running handlers" and if 0, signal the tasks waiting on it to wake up.
8. Drop the lock.

Yes, it can also work, but the fix will go inside IPMI.
And I agree that the codes should not appear in the IPMI context since the issue is highly ACPI interpreter related. What if we just stop doing any further work on this patch and just mark it as RFC or a test patch for information purpose. It is only useful for the testers.

Thanks and best regards
-Lv

Quoted 8 lines

Thanks,
Rafael

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

author
#33 lore raw

PATCH 7/13 ACPI/IPMI: Add reference counting for ACPI IPMI transfers

In reply to Lv Zheng

This patch adds reference counting for ACPI IPMI transfers to tune the locking granularity of tx_msg_lock.

The acpi_ipmi_msg handling is re-designed using referece counting.
1. tx_msg is always unlinked before complete(), so that:
1.1. it is safe to put complete() out side of tx_msg_lock; 1.2. complete() can only happen once, thus smp_wmb() is not required.
2. Increasing the reference of tx_msg before calling ipmi_request_settime() and introducing tx_msg_lock protected ipmi_cancel_tx_msg() so that a complete() can happen in parellel with tx_msg unlinking in the failure cases.
3. tx_msg holds the reference of acpi_ipmi_device so that it can be flushed and freed in the contexts other than acpi_ipmi_space_handler().

The lockdep_chains shows all acpi_ipmi locks are leaf locks after the tuning:
1. ipmi_lock is always leaf:
irq_context: 0
[ffffffff81a943f8] smi_watchers_mutex [ffffffffa06eca60] driver_data.ipmi_lock
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a6678] s_active#103
[ffffffffa06eca60] driver_data.ipmi_lock
2. without this patch applied, lock used by complete() is held after holding tx_msg_lock:
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a6678] s_active#103
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
[ffffffff81e36620] &p->pi_lock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
[ffffffff81e36620] &p->pi_lock
[ffffffff81e5d0a8] &rq->lock
3. with this patch applied, tx_msg_lock is always leaf:
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a66d8] s_active#107
[ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock

---
 drivers/acpi/acpi_ipmi.c |  107 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 77 insertions(+), 30 deletions(-)
drivers/acpi/acpi_ipmi.c +77 -30
index 2a09156..0ee1ea6 100644
@@ -105,6 +105,7 @@ struct acpi_ipmi_msg {
 	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
 	u8	rx_len;
 	struct acpi_ipmi_device *device;
+	atomic_t	refcnt;
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -195,22 +196,47 @@ static struct acpi_ipmi_device *acpi_ipmi_get_selected_smi(void)
 	return ipmi_device;
 }
 
-static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 {
+	struct acpi_ipmi_device *ipmi;
 	struct acpi_ipmi_msg *ipmi_msg;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 
+	ipmi = acpi_ipmi_get_selected_smi();
+	if (!ipmi)
+		return NULL;
 	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");
+	if (!ipmi_msg) {
+		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+	atomic_set(&ipmi_msg->refcnt, 1);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
+
 	return ipmi_msg;
 }
 
+static struct acpi_ipmi_msg *
+acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg)
+		atomic_inc(&tx_msg->refcnt);
+	return tx_msg;
+}
+
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+	acpi_ipmi_dev_put(tx_msg->device);
+	kfree(tx_msg);
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg && atomic_dec_and_test(&tx_msg->refcnt))
+		ipmi_msg_release(tx_msg);
+}
+
 #define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
 #define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
@@ -300,7 +326,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
-	struct acpi_ipmi_msg *tx_msg, *temp;
+	struct acpi_ipmi_msg *tx_msg;
 	unsigned long flags;
 
 	/*
@@ -311,16 +337,46 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	 */
 	while (atomic_read(&ipmi->refcnt) > 1) {
 		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-		list_for_each_entry_safe(tx_msg, temp,
-					 &ipmi->tx_msg_list, head) {
+		while (!list_empty(&ipmi->tx_msg_list)) {
+			tx_msg = list_first_entry(&ipmi->tx_msg_list,
+						  struct acpi_ipmi_msg,
+						  head);
+			list_del(&tx_msg->head);
+			spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 			/* wake up the sleep thread on the Tx msg */
 			complete(&tx_msg->tx_complete);
+			acpi_ipmi_msg_put(tx_msg);
+			spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 		}
 		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 		schedule_timeout_uninterruptible(msecs_to_jiffies(1));
 	}
 }
 
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+			       struct acpi_ipmi_msg *msg)
+{
+	struct acpi_ipmi_msg *tx_msg;
+	int msg_found = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	list_for_each_entry(tx_msg, &ipmi->tx_msg_list, head) {
+		if (msg == tx_msg) {
+			msg_found = 1;
+			break;
+		}
+	}
+	if (msg_found)
+		list_del(&tx_msg->head);
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+	if (msg_found)
+		acpi_ipmi_msg_put(tx_msg);
+}
+
 static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 {
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
@@ -343,12 +399,15 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			break;
 		}
 	}
+	if (msg_found)
+		list_del(&tx_msg->head);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_lock;
+		goto out_msg;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -360,14 +419,11 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
-	/* tx_msg content must be valid before setting msg_done flag */
-	smp_wmb();
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
-out_lock:
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+	acpi_ipmi_msg_put(tx_msg);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }
@@ -493,21 +549,17 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_get_selected_smi();
-	if (!ipmi_device)
+	tx_msg = ipmi_msg_alloc();
+	if (!tx_msg)
 		return AE_NOT_EXIST;
-
-	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg) {
-		status = AE_NO_MEMORY;
-		goto out_ref;
-	}
+	ipmi_device = tx_msg->device;
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
-		status = AE_TYPE;
-		goto out_msg;
+		ipmi_msg_release(tx_msg);
+		return AE_TYPE;
 	}
 
+	acpi_ipmi_msg_get(tx_msg);
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -518,21 +570,16 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 				   NULL, 0, 0, 0);
 	if (err) {
 		status = AE_ERROR;
-		goto out_list;
+		goto out_msg;
 	}
 	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
 					       IPMI_TIMEOUT);
 	acpi_format_ipmi_response(tx_msg, value, rem_time);
 	status = AE_OK;
 
-out_list:
-	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
-	list_del(&tx_msg->head);
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
-	kfree(tx_msg);
-out_ref:
-	acpi_ipmi_dev_put(ipmi_device);
+	ipmi_cancel_tx_msg(ipmi_device, tx_msg);
+	acpi_ipmi_msg_put(tx_msg);
 	return status;
 }
 
Signature · 1 line
1.7.10
#34 lore raw

Re: [PATCH 07/13] ACPI/IPMI: Add reference counting for ACPI IPMI transfers

Quoted 64 lines · Lv Zheng

This patch adds reference counting for ACPI IPMI transfers to tune the locking granularity of tx_msg_lock.

The acpi_ipmi_msg handling is re-designed using referece counting.
1. tx_msg is always unlinked before complete(), so that:
1.1. it is safe to put complete() out side of tx_msg_lock; 1.2. complete() can only happen once, thus smp_wmb() is not required.
2. Increasing the reference of tx_msg before calling ipmi_request_settime() and introducing tx_msg_lock protected ipmi_cancel_tx_msg() so that a complete() can happen in parellel with tx_msg unlinking in the failure cases.
3. tx_msg holds the reference of acpi_ipmi_device so that it can be flushed and freed in the contexts other than acpi_ipmi_space_handler().

The lockdep_chains shows all acpi_ipmi locks are leaf locks after the tuning:
1. ipmi_lock is always leaf:

   irq_context: 0
   [ffffffff81a943f8] smi_watchers_mutex
   [ffffffffa06eca60] driver_data.ipmi_lock
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06eca60] driver_data.ipmi_lock
2. without this patch applied, lock used by complete() is held after
   holding tx_msg_lock:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   [ffffffff81e5d0a8] &rq->lock
3. with this patch applied, tx_msg_lock is always leaf:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a66d8] s_active#107
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
---
 drivers/acpi/acpi_ipmi.c |  107 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 77 insertions(+), 30 deletions(-)
drivers/acpi/acpi_ipmi.c +0 -0
index 2a09156..0ee1ea6 100644

drivers/acpi/acpi_ipmi.c struct acpi_ipmi_msg { · line 108

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -0
@@ -105,6 +105,7 @@ struct acpi_ipmi_msg {
 	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
 	u8	rx_len;
 	struct acpi_ipmi_device *device;
+	atomic_t	refcnt;

Again: kref, please?

Quoted 83 lines
drivers/acpi/acpi_ipmi.c +39 -7
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -195,22 +196,47 @@ static struct acpi_ipmi_device *acpi_ipmi_get_selected_smi(void)
 	return ipmi_device;
 }
 
-static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 {
+	struct acpi_ipmi_device *ipmi;
 	struct acpi_ipmi_msg *ipmi_msg;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 
+	ipmi = acpi_ipmi_get_selected_smi();
+	if (!ipmi)
+		return NULL;
 	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");
+	if (!ipmi_msg) {
+		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+	atomic_set(&ipmi_msg->refcnt, 1);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
+
 	return ipmi_msg;
 }
 
+static struct acpi_ipmi_msg *
+acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg)
+		atomic_inc(&tx_msg->refcnt);
+	return tx_msg;
+}
+
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+	acpi_ipmi_dev_put(tx_msg->device);
+	kfree(tx_msg);
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg && atomic_dec_and_test(&tx_msg->refcnt))
+		ipmi_msg_release(tx_msg);
+}
+
 #define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
 #define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
@@ -300,7 +326,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
-	struct acpi_ipmi_msg *tx_msg, *temp;
+	struct acpi_ipmi_msg *tx_msg;
 	unsigned long flags;
 
 	/*
@@ -311,16 +337,46 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	 */
 	while (atomic_read(&ipmi->refcnt) > 1) {
 		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-		list_for_each_entry_safe(tx_msg, temp,
-					 &ipmi->tx_msg_list, head) {
+		while (!list_empty(&ipmi->tx_msg_list)) {
+			tx_msg = list_first_entry(&ipmi->tx_msg_list,
+						  struct acpi_ipmi_msg,
+						  head);
+			list_del(&tx_msg->head);
+			spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 			/* wake up the sleep thread on the Tx msg */
 			complete(&tx_msg->tx_complete);
+			acpi_ipmi_msg_put(tx_msg);
+			spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 		}
 		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 362

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +6 -0
+
 		schedule_timeout_uninterruptible(msecs_to_jiffies(1));
 	}
 }
 
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+			       struct acpi_ipmi_msg *msg)
+{
+	struct acpi_ipmi_msg *tx_msg;
+	int msg_found = 0;

Use bool?

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 373

Quoted 11 lines
drivers/acpi/acpi_ipmi.c +11 -0
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	list_for_each_entry(tx_msg, &ipmi->tx_msg_list, head) {
+		if (msg == tx_msg) {
+			msg_found = 1;
+			break;
+		}
+	}
+	if (msg_found)
+		list_del(&tx_msg->head);

The list_del() can be done when you set msg_found.

Quoted 96 lines
drivers/acpi/acpi_ipmi.c +20 -23
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+	if (msg_found)
+		acpi_ipmi_msg_put(tx_msg);
+}
+
 static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 {
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
@@ -343,12 +399,15 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			break;
 		}
 	}
+	if (msg_found)
+		list_del(&tx_msg->head);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_lock;
+		goto out_msg;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -360,14 +419,11 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
-	/* tx_msg content must be valid before setting msg_done flag */
-	smp_wmb();
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
-out_lock:
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+	acpi_ipmi_msg_put(tx_msg);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }
@@ -493,21 +549,17 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_get_selected_smi();
-	if (!ipmi_device)
+	tx_msg = ipmi_msg_alloc();
+	if (!tx_msg)
 		return AE_NOT_EXIST;
-
-	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg) {
-		status = AE_NO_MEMORY;
-		goto out_ref;
-	}
+	ipmi_device = tx_msg->device;
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
-		status = AE_TYPE;
-		goto out_msg;
+		ipmi_msg_release(tx_msg);
+		return AE_TYPE;
 	}
 
+	acpi_ipmi_msg_get(tx_msg);
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -518,21 +570,16 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 				   NULL, 0, 0, 0);
 	if (err) {
 		status = AE_ERROR;
-		goto out_list;
+		goto out_msg;
 	}
 	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
 					       IPMI_TIMEOUT);
 	acpi_format_ipmi_response(tx_msg, value, rem_time);
 	status = AE_OK;
 
-out_list:
-	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
-	list_del(&tx_msg->head);
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
-	kfree(tx_msg);
-out_ref:
-	acpi_ipmi_dev_put(ipmi_device);
+	ipmi_cancel_tx_msg(ipmi_device, tx_msg);
+	acpi_ipmi_msg_put(tx_msg);
 	return status;
 }
 
Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#35 lore raw

RE: [PATCH 07/13] ACPI/IPMI: Add reference counting for ACPI IPMI transfers

Quoted 65 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Rafael J. Wysocki
Sent: Friday, July 26, 2013 6:23 AM

Quoted 52 lines · Lv Zheng

This patch adds reference counting for ACPI IPMI transfers to tune the locking granularity of tx_msg_lock.

The acpi_ipmi_msg handling is re-designed using referece counting.
1. tx_msg is always unlinked before complete(), so that:
1.1. it is safe to put complete() out side of tx_msg_lock; 1.2. complete() can only happen once, thus smp_wmb() is not required.
2. Increasing the reference of tx_msg before calling ipmi_request_settime() and introducing tx_msg_lock protected ipmi_cancel_tx_msg() so that a complete() can happen in parellel with tx_msg unlinking in the failure cases.
3. tx_msg holds the reference of acpi_ipmi_device so that it can be flushed and freed in the contexts other than acpi_ipmi_space_handler().

The lockdep_chains shows all acpi_ipmi locks are leaf locks after the tuning:
1. ipmi_lock is always leaf:

   irq_context: 0
   [ffffffff81a943f8] smi_watchers_mutex
   [ffffffffa06eca60] driver_data.ipmi_lock
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06eca60] driver_data.ipmi_lock
2. without this patch applied, lock used by complete() is held after
   holding tx_msg_lock:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   [ffffffff81e5d0a8] &rq->lock
3. with this patch applied, tx_msg_lock is always leaf:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a66d8] s_active#107
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |  107

+++++++++++++++++++++++++++++++++-------------

Quoted 2 lines
 1 file changed, 77 insertions(+), 30 deletions(-)
Quoted 11 lines

drivers/acpi/acpi_ipmi.c struct acpi_ipmi_msg { · line 108

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -0
index 2a09156..0ee1ea6 100644
@@ -105,6 +105,7 @@ struct acpi_ipmi_msg {
 	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
 	u8	rx_len;
 	struct acpi_ipmi_device *device;
+	atomic_t	refcnt;

Again: kref, please?

Please see the concerns in another email.

Quoted 93 lines

drivers/acpi/acpi_ipmi.c static struct acpi_ipmi_device

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -195,22 +196,47 @@ static struct acpi_ipmi_device

*acpi_ipmi_get_selected_smi(void)

drivers/acpi/acpi_ipmi.c static struct acpi_ipmi_device *acpi_ipmi_get_selected_smi(void)

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +0 -1
 	return ipmi_device;
 }
 
-static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device

*ipmi)

drivers/acpi/acpi_ipmi.c static void acpi_format_ipmi_response(struct

Quoted 48 lines
drivers/acpi/acpi_ipmi.c +29 -3
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 {
+	struct acpi_ipmi_device *ipmi;
 	struct acpi_ipmi_msg *ipmi_msg;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 
+	ipmi = acpi_ipmi_get_selected_smi();
+	if (!ipmi)
+		return NULL;
 	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");
+	if (!ipmi_msg) {
+		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+	atomic_set(&ipmi_msg->refcnt, 1);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
+
 	return ipmi_msg;
 }
 
+static struct acpi_ipmi_msg *
+acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg)
+		atomic_inc(&tx_msg->refcnt);
+	return tx_msg;
+}
+
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+	acpi_ipmi_dev_put(tx_msg->device);
+	kfree(tx_msg);
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg && atomic_dec_and_test(&tx_msg->refcnt))
+		ipmi_msg_release(tx_msg);
+}
+
 #define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
 #define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
@@ -300,7 +326,7 @@ static void acpi_format_ipmi_response(struct

acpi_ipmi_msg *msg,

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
-	struct acpi_ipmi_msg *tx_msg, *temp;
+	struct acpi_ipmi_msg *tx_msg;
 	unsigned long flags;
 
 	/*
@@ -311,16 +337,46 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

Quoted 23 lines
drivers/acpi/acpi_ipmi.c +10 -2
 	 */
 	while (atomic_read(&ipmi->refcnt) > 1) {
 		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-		list_for_each_entry_safe(tx_msg, temp,
-					 &ipmi->tx_msg_list, head) {
+		while (!list_empty(&ipmi->tx_msg_list)) {
+			tx_msg = list_first_entry(&ipmi->tx_msg_list,
+						  struct acpi_ipmi_msg,
+						  head);
+			list_del(&tx_msg->head);
+			spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 			/* wake up the sleep thread on the Tx msg */
 			complete(&tx_msg->tx_complete);
+			acpi_ipmi_msg_put(tx_msg);
+			spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 		}
 		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 		schedule_timeout_uninterruptible(msecs_to_jiffies(1));
 	}
 }
 
Quoted 7 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 362

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +5 -0
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+			       struct acpi_ipmi_msg *msg)
+{
+	struct acpi_ipmi_msg *tx_msg;
+	int msg_found = 0;

Use bool?

OK.
There are other int flags in the original codes, do I need to do a cleanup for all of them (dev_found)?

Quoted 14 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 373

Quoted 12 lines
drivers/acpi/acpi_ipmi.c +11 -0
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	list_for_each_entry(tx_msg, &ipmi->tx_msg_list, head) {
+		if (msg == tx_msg) {
+			msg_found = 1;
+			break;
+		}
+	}
+	if (msg_found)
+		list_del(&tx_msg->head);

The list_del() can be done when you set msg_found.

Please see my concerns in another email.

Thanks and best regards
-Lv

Quoted 109 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)

Quoted 8 lines
drivers/acpi/acpi_ipmi.c +6 -0
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+	if (msg_found)
+		acpi_ipmi_msg_put(tx_msg);
+}
+
 static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void

*user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +0 -0
 {
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
@@ -343,12 +399,15 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 17 lines
drivers/acpi/acpi_ipmi.c +4 -1
 			break;
 		}
 	}
+	if (msg_found)
+		list_del(&tx_msg->head);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
-		goto out_lock;
+		goto out_msg;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -360,14 +419,11 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

drivers/acpi/acpi_ipmi.c acpi_ipmi_space_handler(u32 function,

Quoted 16 lines
drivers/acpi/acpi_ipmi.c +1 -4
 	}
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
-	/* tx_msg content must be valid before setting msg_done flag */
-	smp_wmb();
 	tx_msg->msg_done = 1;
 
 out_comp:
 	complete(&tx_msg->tx_complete);
-out_lock:
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+	acpi_ipmi_msg_put(tx_msg);
 out_msg:
 	ipmi_free_recv_msg(msg);
 }
@@ -493,21 +549,17 @@ acpi_ipmi_space_handler(u32 function,

acpi_physical_address address,

drivers/acpi/acpi_ipmi.c acpi_ipmi_space_handler(u32 function,

Quoted 28 lines
drivers/acpi/acpi_ipmi.c +6 -10
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_get_selected_smi();
-	if (!ipmi_device)
+	tx_msg = ipmi_msg_alloc();
+	if (!tx_msg)
 		return AE_NOT_EXIST;
-
-	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg) {
-		status = AE_NO_MEMORY;
-		goto out_ref;
-	}
+	ipmi_device = tx_msg->device;
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
-		status = AE_TYPE;
-		goto out_msg;
+		ipmi_msg_release(tx_msg);
+		return AE_TYPE;
 	}
 
+	acpi_ipmi_msg_get(tx_msg);
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -518,21 +570,16 @@ acpi_ipmi_space_handler(u32 function,

acpi_physical_address address,

Quoted 15 lines
drivers/acpi/acpi_ipmi.c +1 -4
 				   NULL, 0, 0, 0);
 	if (err) {
 		status = AE_ERROR;
-		goto out_list;
+		goto out_msg;
 	}
 	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
 					       IPMI_TIMEOUT);
 	acpi_format_ipmi_response(tx_msg, value, rem_time);
 	status = AE_OK;
 
-out_list:
-	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
-	list_del(&tx_msg->head);

drivers/acpi/acpi_ipmi.c acpi_ipmi_space_handler(u32 function, acpi_physical_address address, · line 585

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +2 -4
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
-	kfree(tx_msg);
-out_ref:
-	acpi_ipmi_dev_put(ipmi_device);
+	ipmi_cancel_tx_msg(ipmi_device, tx_msg);
+	acpi_ipmi_msg_put(tx_msg);
 	return status;
 }
 

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#36 lore raw

Re: [PATCH 07/13] ACPI/IPMI: Add reference counting for ACPI IPMI transfers

Quoted 180 lines · Lv
Quoted 65 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Rafael J. Wysocki
Sent: Friday, July 26, 2013 6:23 AM

Quoted 52 lines · Lv Zheng

This patch adds reference counting for ACPI IPMI transfers to tune the locking granularity of tx_msg_lock.

The acpi_ipmi_msg handling is re-designed using referece counting.
1. tx_msg is always unlinked before complete(), so that:
1.1. it is safe to put complete() out side of tx_msg_lock; 1.2. complete() can only happen once, thus smp_wmb() is not required.
2. Increasing the reference of tx_msg before calling ipmi_request_settime() and introducing tx_msg_lock protected ipmi_cancel_tx_msg() so that a complete() can happen in parellel with tx_msg unlinking in the failure cases.
3. tx_msg holds the reference of acpi_ipmi_device so that it can be flushed and freed in the contexts other than acpi_ipmi_space_handler().

The lockdep_chains shows all acpi_ipmi locks are leaf locks after the tuning:
1. ipmi_lock is always leaf:

   irq_context: 0
   [ffffffff81a943f8] smi_watchers_mutex
   [ffffffffa06eca60] driver_data.ipmi_lock
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06eca60] driver_data.ipmi_lock
2. without this patch applied, lock used by complete() is held after
   holding tx_msg_lock:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a6678] s_active#103
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   irq_context: 1
   [ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
   [ffffffffa06eccf0] &x->wait#25
   [ffffffff81e36620] &p->pi_lock
   [ffffffff81e5d0a8] &rq->lock
3. with this patch applied, tx_msg_lock is always leaf:
   irq_context: 0
   [ffffffff82767b40] &buffer->mutex
   [ffffffffa00a66d8] s_active#107
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
   irq_context: 1
   [ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
Quoted 5 lines
---
 drivers/acpi/acpi_ipmi.c |  107

+++++++++++++++++++++++++++++++++-------------

Quoted 2 lines
 1 file changed, 77 insertions(+), 30 deletions(-)
Quoted 11 lines

drivers/acpi/acpi_ipmi.c struct acpi_ipmi_msg { · line 108

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -0
index 2a09156..0ee1ea6 100644
@@ -105,6 +105,7 @@ struct acpi_ipmi_msg {
 	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
 	u8	rx_len;
 	struct acpi_ipmi_device *device;
+	atomic_t	refcnt;

Again: kref, please?

Please see the concerns in another email.

Quoted 93 lines

drivers/acpi/acpi_ipmi.c static struct acpi_ipmi_device

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -195,22 +196,47 @@ static struct acpi_ipmi_device

*acpi_ipmi_get_selected_smi(void)

drivers/acpi/acpi_ipmi.c static struct acpi_ipmi_device *acpi_ipmi_get_selected_smi(void)

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +0 -1
 	return ipmi_device;
 }
 
-static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device

*ipmi)

drivers/acpi/acpi_ipmi.c static void acpi_format_ipmi_response(struct

Quoted 48 lines
drivers/acpi/acpi_ipmi.c +29 -3
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 {
+	struct acpi_ipmi_device *ipmi;
 	struct acpi_ipmi_msg *ipmi_msg;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 
+	ipmi = acpi_ipmi_get_selected_smi();
+	if (!ipmi)
+		return NULL;
 	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");
+	if (!ipmi_msg) {
+		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+	atomic_set(&ipmi_msg->refcnt, 1);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
+
 	return ipmi_msg;
 }
 
+static struct acpi_ipmi_msg *
+acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg)
+		atomic_inc(&tx_msg->refcnt);
+	return tx_msg;
+}
+
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+	acpi_ipmi_dev_put(tx_msg->device);
+	kfree(tx_msg);
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+	if (tx_msg && atomic_dec_and_test(&tx_msg->refcnt))
+		ipmi_msg_release(tx_msg);
+}
+
 #define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
 #define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
@@ -300,7 +326,7 @@ static void acpi_format_ipmi_response(struct

acpi_ipmi_msg *msg,

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
-	struct acpi_ipmi_msg *tx_msg, *temp;
+	struct acpi_ipmi_msg *tx_msg;
 	unsigned long flags;
 
 	/*
@@ -311,16 +337,46 @@ static void ipmi_flush_tx_msg(struct

acpi_ipmi_device *ipmi)

Quoted 23 lines
drivers/acpi/acpi_ipmi.c +10 -2
 	 */
 	while (atomic_read(&ipmi->refcnt) > 1) {
 		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-		list_for_each_entry_safe(tx_msg, temp,
-					 &ipmi->tx_msg_list, head) {
+		while (!list_empty(&ipmi->tx_msg_list)) {
+			tx_msg = list_first_entry(&ipmi->tx_msg_list,
+						  struct acpi_ipmi_msg,
+						  head);
+			list_del(&tx_msg->head);
+			spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 			/* wake up the sleep thread on the Tx msg */
 			complete(&tx_msg->tx_complete);
+			acpi_ipmi_msg_put(tx_msg);
+			spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 		}
 		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 		schedule_timeout_uninterruptible(msecs_to_jiffies(1));
 	}
 }
 
Quoted 7 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 362

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +5 -0
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+			       struct acpi_ipmi_msg *msg)
+{
+	struct acpi_ipmi_msg *tx_msg;
+	int msg_found = 0;

Use bool?

Quoted 2 lines

OK.
There are other int flags in the original codes, do I need to do a cleanup for all of them (dev_found)?

Not in this patch, but in general it wouldn't hurt.

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
drivers/acpi/acpi_ipmi.c +5 -0
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	list_for_each_entry(tx_msg, &ipmi->tx_msg_list, head) {
+		if (msg == tx_msg) {
Quoted 8 lines

drivers/acpi/acpi_ipmi.c static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) · line 373

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +6 -0
+			msg_found = 1;
+			break;
+		}
+	}
+	if (msg_found)
+		list_del(&tx_msg->head);

The list_del() can be done when you set msg_found.

Quoted 1 line

Please see my concerns in another email.

OK, I'll reply there.

Thanks,
Rafael

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#37 lore raw

PATCH 8/13 ACPI/IPMI: Cleanup several acpi_ipmi_device members

In reply to Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only used by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/acpi/acpi_ipmi.c +14 -16
index 0ee1ea6..7f93ffd 100644
@@ -63,11 +63,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
 
@@ -132,7 +131,7 @@ static struct ipmi_driver_data driver_data = {
 };
 
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+ipmi_dev_alloc(int iface, struct device *pdev, acpi_handle handle)
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -148,14 +147,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->dev = get_device(pdev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(pdev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -175,7 +173,7 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device)
 static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
 {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -263,7 +261,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -382,11 +380,11 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response is returned. returned user %p, expected user %p\n",
 			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -404,7 +402,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
 		goto out_msg;
@@ -412,7 +410,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
 		goto out_comp;
@@ -431,7 +429,7 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
+	struct device *pdev;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -445,11 +443,11 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);
+	pdev = smi_data.dev;
 
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, pdev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		dev_warn(pdev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 
Signature · 1 line
1.7.10
#38 lore raw

Re: [PATCH 08/13] ACPI/IPMI: Cleanup several acpi_ipmi_device members

Quoted 30 lines · Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only used by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/acpi/acpi_ipmi.c +1 -2
index 0ee1ea6..7f93ffd 100644
@@ -63,11 +63,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
 

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 134

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +1 -1
@@ -132,7 +131,7 @@ static struct ipmi_driver_data driver_data = {
 };
 
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+ipmi_dev_alloc(int iface, struct device *pdev, acpi_handle handle)

Why is the second arg called pdev?

Quoted 71 lines
drivers/acpi/acpi_ipmi.c +8 -9
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -148,14 +147,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->dev = get_device(pdev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(pdev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -175,7 +173,7 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device *ipmi_device)
 static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
 {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -263,7 +261,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -382,11 +380,11 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response is returned. returned user %p, expected user %p\n",
 			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -404,7 +402,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
 		goto out_msg;
@@ -412,7 +410,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
 		goto out_comp;

drivers/acpi/acpi_ipmi.c out_msg: · line 432

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +1 -1
@@ -431,7 +429,7 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
+	struct device *pdev;

And here?

Quoted 19 lines
drivers/acpi/acpi_ipmi.c +3 -3
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -445,11 +443,11 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);
+	pdev = smi_data.dev;
 
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, pdev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		dev_warn(pdev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 
Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#39 lore raw

RE: [PATCH 08/13] ACPI/IPMI: Cleanup several acpi_ipmi_device members

Quoted 33 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 6:26 AM

Quoted 29 lines · Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only used by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

0ee1ea6..7f93ffd 100644

drivers/acpi/acpi_ipmi.c +1 -2
@@ -63,11 +63,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
 
Quoted 8 lines

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { }; · line 134

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +1 -1
@@ -132,7 +131,7 @@ static struct ipmi_driver_data driver_data = {  };
 
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle
 handle)
+ipmi_dev_alloc(int iface, struct device *pdev, acpi_handle handle)

Why is the second arg called pdev?

OK, I will change it to dev.

Quoted 71 lines

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -148,14 +147,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info

*smi_data, acpi_handle handle)

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -2
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct

ipmi_smi_info));

drivers/acpi/acpi_ipmi.c static int acpi_format_ipmi_request(struct

Quoted 21 lines
drivers/acpi/acpi_ipmi.c +3 -2
+	ipmi_device->dev = get_device(pdev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(pdev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -175,7 +173,7 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device
 *ipmi_device)  static void ipmi_dev_release(struct acpi_ipmi_device
 *ipmi_device)  {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -263,7 +261,7 @@ static int acpi_format_ipmi_request(struct

acpi_ipmi_msg *tx_msg,

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -382,11 +380,11 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)

Quoted 11 lines
drivers/acpi/acpi_ipmi.c +2 -2
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response is returned. returned user %p, expected

user %p\n",

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +0 -0
 			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -404,7 +402,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg

*msg, void *user_msg_data)

Quoted 11 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
 		goto out_msg;
@@ -412,7 +410,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg
 *msg, void *user_msg_data)
 
Quoted 14 lines

drivers/acpi/acpi_ipmi.c out_msg: · line 431

Quoted 12 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
drivers/acpi/acpi_ipmi.c +1 -1
 		goto out_comp;
@@ -431,7 +429,7 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)  {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
+	struct device *pdev;

And here?

The dev is the parameter of the ipmi_register_bmc(), it is not possible to name the "struct ipmi_smi_info " as dev here for this quick fix.

Thanks
-Lv

Quoted 12 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -445,11 +443,11 @@ static void ipmi_register_bmc(int iface, struct

device *dev)

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);
+	pdev = smi_data.dev;
 
Quoted 12 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev) · line 453

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +2 -2
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, pdev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		dev_warn(pdev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk

#40 lore raw

Re: [PATCH 08/13] ACPI/IPMI: Cleanup several acpi_ipmi_device members

Quoted 35 lines · Lv
Quoted 35 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 6:26 AM

Quoted 31 lines · Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only used by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

0ee1ea6..7f93ffd 100644

drivers/acpi/acpi_ipmi.c +1 -2
@@ -63,11 +63,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
 
@@ -132,7 +131,7 @@ static struct ipmi_driver_data driver_data = {  };
 
Quoted 8 lines
Quoted 6 lines

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 134

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -1
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle
 handle)
+ipmi_dev_alloc(int iface, struct device *pdev, acpi_handle handle)

Why is the second arg called pdev?

OK, I will change it to dev.

OK, thanks.

Quoted 86 lines
Quoted 71 lines

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -148,14 +147,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info

*smi_data, acpi_handle handle)

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -2
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct

ipmi_smi_info));

drivers/acpi/acpi_ipmi.c static int acpi_format_ipmi_request(struct

Quoted 21 lines
drivers/acpi/acpi_ipmi.c +3 -2
+	ipmi_device->dev = get_device(pdev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(pdev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -175,7 +173,7 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device
 *ipmi_device)  static void ipmi_dev_release(struct acpi_ipmi_device
 *ipmi_device)  {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -263,7 +261,7 @@ static int acpi_format_ipmi_request(struct

acpi_ipmi_msg *tx_msg,

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -382,11 +380,11 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)

Quoted 11 lines
drivers/acpi/acpi_ipmi.c +2 -2
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response is returned. returned user %p, expected

user %p\n",

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +0 -0
 			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -404,7 +402,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg

*msg, void *user_msg_data)

Quoted 11 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
 		goto out_msg;
@@ -412,7 +410,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg
 *msg, void *user_msg_data)
 
Quoted 14 lines

drivers/acpi/acpi_ipmi.c out_msg: · line 431

Quoted 12 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
drivers/acpi/acpi_ipmi.c +1 -1
 		goto out_comp;
@@ -431,7 +429,7 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)  {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
+	struct device *pdev;

And here?

Quoted 1 line

The dev is the parameter of the ipmi_register_bmc(), it is not possible to name the "struct ipmi_smi_info " as dev here for this quick fix.

Right. What about smi_dev? Or just use smi_data.dev directly? It's just two places and shouldn't cause any line wraps to happen.

Rafael

Quoted 24 lines
Quoted 23 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +0 -0
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -445,11 +443,11 @@ static void ipmi_register_bmc(int iface, struct

device *dev)

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -1
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev) · line 453

Quoted 10 lines
drivers/acpi/acpi_ipmi.c +3 -2
+	pdev = smi_data.dev;
 
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, pdev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		dev_warn(pdev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
author
#41 lore raw

RE: [PATCH 08/13] ACPI/IPMI: Cleanup several acpi_ipmi_device members

Quoted 140 lines

From: [email protected] [mailto:[email protected]] On Behalf Of Rafael J. Wysocki

Quoted 8 lines · Lv
Quoted 8 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Friday, July 26, 2013 6:26 AM

Quoted 4 lines · Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only

used

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines

by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/acpi/acpi_ipmi.c index renamed from drivers/acpi/acpi_ipmi.c +0 -0

0ee1ea6..7f93ffd 100644

drivers/acpi/acpi_ipmi.c +1 -2
@@ -63,11 +63,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	atomic_t refcnt;
 };
 
@@ -132,7 +131,7 @@ static struct ipmi_driver_data driver_data = {  };
 
Quoted 8 lines
Quoted 6 lines

drivers/acpi/acpi_ipmi.c static struct ipmi_driver_data driver_data = { · line 134

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -1
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle
 handle)
+ipmi_dev_alloc(int iface, struct device *pdev, acpi_handle handle)

Why is the second arg called pdev?

OK, I will change it to dev.

OK, thanks.

Quoted 51 lines
Quoted 51 lines

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -0
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -148,14 +147,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info

*smi_data, acpi_handle handle)

drivers/acpi/acpi_ipmi.c ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +0 -2
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct

ipmi_smi_info));

drivers/acpi/acpi_ipmi.c static int acpi_format_ipmi_request(struct

Quoted 21 lines
drivers/acpi/acpi_ipmi.c +3 -2
+	ipmi_device->dev = get_device(pdev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(pdev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -175,7 +173,7 @@ acpi_ipmi_dev_get(struct acpi_ipmi_device
 *ipmi_device)  static void ipmi_dev_release(struct acpi_ipmi_device
 *ipmi_device)  {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -263,7 +261,7 @@ static int acpi_format_ipmi_request(struct

acpi_ipmi_msg *tx_msg,

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -382,11 +380,11 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg *msg, void *user_msg_data)

Quoted 7 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
Quoted 4 lines
Quoted 4 lines

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response is returned. returned user %p,

expected

Quoted 4 lines
Quoted 4 lines

user %p\n",

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 3 lines
drivers/acpi/acpi_ipmi.c +0 -0
 			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -404,7 +402,7 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg

Quoted 10 lines
Quoted 10 lines

*msg, void *user_msg_data)

drivers/acpi/acpi_ipmi.c static void ipmi_msg_handler(struct

Quoted 9 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev,
+		dev_warn(dev,
 			 "Unexpected response (msg id %ld) is returned.\n",
 			 msg->msgid);
 		goto out_msg;
@@ -412,7 +410,7 @@ static void ipmi_msg_handler(struct

ipmi_recv_msg

Quoted 17 lines
Quoted 16 lines
Quoted 10 lines
drivers/acpi/acpi_ipmi.c +1 -1
 *msg, void *user_msg_data)
 
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
drivers/acpi/acpi_ipmi.c +0 -0
 		goto out_comp;

drivers/acpi/acpi_ipmi.c out_msg: · line 431

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -1
@@ -431,7 +429,7 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)  {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
+	struct device *pdev;

And here?

Quoted 6 lines
Quoted 1 line

The dev is the parameter of the ipmi_register_bmc(), it is not possible to

name the "struct ipmi_smi_info " as dev here for this quick fix.

Right. What about smi_dev? Or just use smi_data.dev directly? It's just two
places and shouldn't cause any line wraps to happen.

Sounds good, I'll take your advice. :-)

Thanks and best regards
-Lv

Quoted 36 lines

Rafael

Quoted 11 lines
Quoted 11 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +0 -0
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -445,11 +443,11 @@ static void ipmi_register_bmc(int iface, struct

device *dev)

Quoted 6 lines
drivers/acpi/acpi_ipmi.c +1 -1
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);
+	pdev = smi_data.dev;
 
Quoted 4 lines
Quoted 4 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev)

Quoted 4 lines
drivers/acpi/acpi_ipmi.c +1 -2
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, pdev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user

interface\n");

Quoted 9 lines
Quoted 8 lines

drivers/acpi/acpi_ipmi.c static void ipmi_register_bmc(int iface, struct device *dev) · line 453

Quoted 5 lines
drivers/acpi/acpi_ipmi.c +1 -0
+		dev_warn(pdev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#42 lore raw

PATCH 9/13 ACPI/IPMI: Cleanup some initialization codes

In reply to Lv Zheng

This is a trivial patch.
1. Changes dynamic mutex initialization to static initialization.
2. Removes one acpi_ipmi_init() variable initialization as it is not needed.

---
 drivers/acpi/acpi_ipmi.c |    7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/acpi/acpi_ipmi.c +3 -4
index 7f93ffd..2d31003 100644
@@ -128,6 +128,7 @@ static struct ipmi_driver_data driver_data = {
 	.ipmi_hndlrs = {
 		.ipmi_recv_hndl = ipmi_msg_handler,
 	},
+	.ipmi_lock = __MUTEX_INITIALIZER(driver_data.ipmi_lock)
 };
 
 static struct acpi_ipmi_device *
@@ -583,12 +584,10 @@ out_msg:
 
 static int __init acpi_ipmi_init(void)
 {
-	int result = 0;
+	int result;
 
 	if (acpi_disabled)
-		return result;
-
-	mutex_init(&driver_data.ipmi_lock);
+		return 0;
 
 	result = acpi_register_region(ACPI_ADR_SPACE_IPMI,
 				      &acpi_ipmi_space_handler,
Signature · 1 line
1.7.10
author
#43 lore raw

PATCH 10/13 ACPI/IPMI: Cleanup some inclusion codes

In reply to Lv Zheng

This is a trivial patch:
1. Deletes several useless header inclusions.
2. Kernel codes should always include <linux/acpi.h> instead of <acpi/acpi_bus.h> or <acpi/acpi_drivers.h> where many conditional declarations are handled.

---
 drivers/acpi/acpi_ipmi.c |   15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)
drivers/acpi/acpi_ipmi.c +1 -14
index 2d31003..e56b1f8 100644
@@ -24,22 +24,9 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
-#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/acpi.h>
 #include <linux/ipmi.h>
-#include <linux/device.h>
-#include <linux/pnp.h>
 #include <linux/spinlock.h>
 
 MODULE_AUTHOR("Zhao Yakui");
Signature · 1 line
1.7.10
author
#44 lore raw

PATCH 11/13 ACPI/IPMI: Cleanup some Kconfig codes

In reply to Lv Zheng

This is a trivial patch:
1. Deletes duplicate Kconfig dependency as there is "if IPMI_HANDLER" around "IPMI_SI".

---
 drivers/acpi/Kconfig |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/acpi/Kconfig +2 -1
index 3278a21..d129869 100644
@@ -181,9 +181,10 @@ 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 IPMI_SI && IPMI_HANDLER
+	depends on IPMI_SI
 	default n
 	help
 	  This driver enables the ACPI to access the BMC controller. And it
Signature · 1 line
1.7.10
author
#45 lore raw

PATCH 12/13 Testing: Add module load/unload test suite

In reply to Lv Zheng

This patch contains two test scripts for module load/unload race testing.

Follow the following steps to use this test suite:
1. Run several instances invoking endless_cat.sh to access the sysfs files that exported by the module.
2. Run endless_mod.sh to load/unload the module frequently to see if races will happen.

---
 tools/testing/module-unloading/endless_cat.sh |   32 ++++++++++
 tools/testing/module-unloading/endless_mod.sh |   81 +++++++++++++++++++++++++
 2 files changed, 113 insertions(+)
 create mode 100755 tools/testing/module-unloading/endless_cat.sh
 create mode 100755 tools/testing/module-unloading/endless_mod.sh
tools/testing/module-unloading/endless_cat.sh new file +32 -0
new file mode 100755
index 0000000..72e035c
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+fatal() {
+	echo $1
+	exit 1
+}
+
+usage() {
+	echo "Usage: `basename $0` <file>"
+	echo "Where:"
+	echo " file: file to cat"
+}
+
+fatal_usage() {
+	usage
+	exit 1
+}
+
+if [ "x$1" = "x" ]; then
+	echo "Missing <file> paraemter."
+	fatal_usage
+fi
+if [ ! -f $1 ]; then
+	echo "$1 is not an accessible file."
+	fatal_usage
+fi
+
+while :
+do
+	cat $1
+	echo "-----------------------------------"
+done
tools/testing/module-unloading/endless_mod.sh new file +81 -0

81 lines not on this page · raw message

Signature · 1 line
1.7.10
author
#46 lore raw

PATCH 13/13 ACPI/IPMI: Add IPMI operation region test device driver

In reply to Lv Zheng

This patch is only used for test purpose and should not be merged by any public Linux kernel repositories.

This patch contains one driver that can drive a fake test device accessing IPMI operation region fields.

---
 drivers/acpi/Kconfig     |   68 +++++++++++++
 drivers/acpi/Makefile    |    1 +
 drivers/acpi/ipmi_test.c |  254 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 323 insertions(+)
 create mode 100644 drivers/acpi/ipmi_test.c
drivers/acpi/Kconfig +68 -0
index d129869..e3dd3fd 100644
@@ -377,6 +377,74 @@ config ACPI_BGRT
 	  data from the firmware boot splash. It will appear under
 	  /sys/firmware/acpi/bgrt/ .
 
+config ACPI_IPMI_TEST
+	tristate "IPMI operation region tester"
+	help
+	  This is a test device written for such fake ACPI namespace device.
+	    Device (PMIT)
+	    {
+	        Name (_HID, "ZETA0000")  // _HID: Hardware ID
+	        Name (_STA, 0x0F)  // _STA: Status
+	        OperationRegion (SYSI, IPMI, 0x0600, 0x0100)
+	        Field (SYSI, BufferAcc, Lock, Preserve)
+	        {
+	            AccessAs (BufferAcc, 0x01),
+                   Offset (0x01),
+	            GDIC,   8,	// Get Device ID Command
+	        }
+	        Method (GDIM, 0, NotSerialized)  // GDIM: Get Device ID Method
+	        {
+	            Name (GDIR, Package (0x08)
+	            {
+	                0x00,
+	                0x00,
+	                0x0000,
+	                0x00,
+	                0x00,
+	                Buffer (0x03) {0x00, 0x00, 0x00},
+	                Buffer (0x02) {0x00, 0x00},
+	                0x00000000
+	            })
+	            Name (BUFF, Buffer (0x42) {})
+	            CreateByteField (BUFF, 0x00, STAT)
+	            CreateByteField (BUFF, 0x01, LENG)
+	            CreateByteField (BUFF, 0x02, CMPC)
+	            CreateByteField (BUFF, 0x03, DID)
+	            CreateByteField (BUFF, 0x04, DREV)
+	            CreateWordField (BUFF, 0x05, FREV)
+	            CreateByteField (BUFF, 0x07, SREV)
+	            CreateByteField (BUFF, 0x08, ADS)
+	            CreateByteField (BUFF, 0x09, VID0)
+	            CreateByteField (BUFF, 0x0A, VID1)
+	            CreateByteField (BUFF, 0x0B, VID2)
+	            CreateByteField (BUFF, 0x0C, PID0)
+	            CreateByteField (BUFF, 0x0D, PID1)
+	            CreateDWordField (BUFF, 0x0E, AFRI)
+	            Store (0x00, LENG)
+	            Store (Store (BUFF, GDIC), BUFF)
+	            If (LAnd (LEqual (STAT, 0x00), LEqual (CMPC, 0x00)))
+	            {
+	                Name (VBUF, Buffer (0x03) { 0x00, 0x00, 0x00 })
+	                Name (PBUF, Buffer (0x02) { 0x00, 0x00 })
+	                Store (DID, Index (GDIR, 0x00))
+	                Store (DREV, Index (GDIR, 0x01))
+	                Store (FREV, Index (GDIR, 0x02))
+	                Store (SREV, Index (GDIR, 0x03))
+	                Store (ADS, Index (GDIR, 0x04))
+	                Store (VID0, Index (VBUF, 0x00))
+	                Store (VID1, Index (VBUF, 0x01))
+	                Store (VID2, Index (VBUF, 0x02))
+	                Store (VBUF, Index (GDIR, 0x05))
+	                Store (PID0, Index (PBUF, 0x00))
+	                Store (PID1, Index (PBUF, 0x01))
+	                Store (PBUF, Index (GDIR, 0x06))
+	                Store (AFRI, Index (GDIR, 0x07))
+	            }
+	            Return (GDIR)
+	        }
+	    }
+	  It is for validation purpose, only calls "Get Device ID" command.
+
 source "drivers/acpi/apei/Kconfig"
 
 endif	# ACPI
drivers/acpi/Makefile +1 -0
index 81dbeb8..1476623 100644
@@ -74,6 +74,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS)	+= ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
 obj-$(CONFIG_ACPI_BGRT)		+= bgrt.o
 obj-$(CONFIG_ACPI_I2C)		+= acpi_i2c.o
+obj-$(CONFIG_ACPI_IPMI_TEST)	+= ipmi_test.o
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o processor_throttling.o
drivers/acpi/ipmi_test.c new file +254 -0

254 lines not on this page · raw message

Signature · 1 line
1.7.10
author
#47 lore raw

v2 cover ACPI/IPMI: Fix several issues in the current codes

In reply to a message not in the archive

This patchset tries to fix the following kernel bug:

This is fixed by [PATCH 06].

The bug shows IPMI operation region may appear in a device not under the IPMI system interface device's scope, thus it's required to install the ACPI IPMI operation region handler from the root of the ACPI namespace.

The original acpi_ipmi implementation includes several issues that break the test process. This patchset also includes a re-design of acpi_ipmi module to make the test possible.

[PATCH 01-06] are bug-fix patches that can be applied to the kernels whose
version is > 2.6.38. This can be confirmed with:
# git tag --contains e92b297c
[PATCH 07] is a tuning patch for acpi_ipmi.c.
[PATCH 08-12] are cleanup patches for acpi_ipmi.c and its Kconfig item.

v2.0
PATCH 03: Uses timeout mechanism offerred by ipmi_si.
PATCH 05: Uses kref instead of atomic_t and adds "dead" flag to kill the
atomic_read codes in ipmi_flush_tx_msg().
PATCH 07: Uses kref instead of atomic_t.

This patchset has passed the test around a fake device accessing IPMI operation region fields on an IPMI capable platform. A stress test of module(acpi_ipmi) load/unload has been performed on such platform. No races can be found and the IPMI operation region handler is functioning now. It is not possible to test module(ipmi_si) load/unload as it can't be unloaded due to its' transfer flushing implementation.

Lv Zheng (12):
ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler() ACPI/IPMI: Fix potential response buffer overflow ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers ACPI/IPMI: Fix race caused by the timed out ACPI IPMI transfers ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user ACPI/IPMI: Fix issue caused by the per-device registration of the
IPMI operation region handler
ACPI/IPMI: Add reference counting for ACPI IPMI transfers ACPI/IPMI: Cleanup several acpi_ipmi_device members ACPI/IPMI: Cleanup some initialization codes ACPI/IPMI: Cleanup some inclusion codes
ACPI/IPMI: Cleanup some Kconfig codes
ACPI/IPMI: Cleanup coding styles

 drivers/acpi/Kconfig     |    3 +-
 drivers/acpi/acpi_ipmi.c |  594 ++++++++++++++++++++++++++++------------------
 2 files changed, 367 insertions(+), 230 deletions(-)
Signature · 1 line
1.7.10
author
#48 lore raw

PATCH v2 1/12 ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler()

This patch quick fixes the issues indicated by the test results that ipmi_msg_handler() is invoked in atomic context.

BUG: scheduling while atomic: kipmi0/18933/0x10000100
Modules linked in: ipmi_si acpi_ipmi ...
CPU: 3 PID: 18933 Comm: kipmi0 Tainted: G       AW    3.10.0-rc7+ #2
Hardware name: QCI QSSC-S4R/QSSC-S4R, BIOS QSSC-S4R.QCI.01.00.0027.070120100606 07/01/2010
 ffff8838245eea00 ffff88103fc63c98 ffffffff814c4a1e ffff88103fc63ca8
 ffffffff814bfbab ffff88103fc63d28 ffffffff814c73e0 ffff88103933cbd4
 0000000000000096 ffff88103fc63ce8 ffff88102f618000 ffff881035c01fd8
Call Trace:
 <IRQ>  [<ffffffff814c4a1e>] dump_stack+0x19/0x1b
 [<ffffffff814bfbab>] __schedule_bug+0x46/0x54
 [<ffffffff814c73e0>] __schedule+0x83/0x59c
 [<ffffffff81058853>] __cond_resched+0x22/0x2d
 [<ffffffff814c794b>] _cond_resched+0x14/0x1d
 [<ffffffff814c6d82>] mutex_lock+0x11/0x32
 [<ffffffff8101e1e9>] ? __default_send_IPI_dest_field.constprop.0+0x53/0x58
 [<ffffffffa09e3f9c>] ipmi_msg_handler+0x23/0x166 [ipmi_si]
 [<ffffffff812bf6e4>] deliver_response+0x55/0x5a
 [<ffffffff812c0fd4>] handle_new_recv_msgs+0xb67/0xc65
 [<ffffffff81007ad1>] ? read_tsc+0x9/0x19
 [<ffffffff814c8620>] ? _raw_spin_lock_irq+0xa/0xc
 [<ffffffffa09e1128>] ipmi_thread+0x5c/0x146 [ipmi_si]
 ...

Known issues:
- Replacing tx_msg_lock with spinlock is not performance friendly Current solution works but does not have the best performance because it is better to make atomic context run as fast as possible. Given there are no many IPMI messages created by ACPI, performance of current solution may be OK. It can be better via linking ipmi_recv_msg into an RX message queue and process it in other contexts.

---
 drivers/acpi/acpi_ipmi.c |   24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)
drivers/acpi/acpi_ipmi.c +14 -10
index f40acef..a6977e1 100644
@@ -39,6 +39,7 @@
 #include <linux/ipmi.h>
 #include <linux/device.h>
 #include <linux/pnp.h>
+#include <linux/spinlock.h>
 
 MODULE_AUTHOR("Zhao Yakui");
 MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
@@ -57,7 +58,7 @@ struct acpi_ipmi_device {
 	struct list_head head;
 	/* the IPMI request message list */
 	struct list_head tx_msg_list;
-	struct mutex	tx_msg_lock;
+	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
 	struct pnp_dev *pnp_dev;
 	ipmi_user_t	user_interface;
@@ -147,6 +148,7 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 	struct kernel_ipmi_msg *msg;
 	struct acpi_ipmi_buffer *buffer;
 	struct acpi_ipmi_device *device;
+	unsigned long flags;
 
 	msg = &tx_msg->tx_message;
 	/*
@@ -177,10 +179,10 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 
 	/* Get the msgid */
 	device = tx_msg->device;
-	mutex_lock(&device->tx_msg_lock);
+	spin_lock_irqsave(&device->tx_msg_lock, flags);
 	device->curr_msgid++;
 	tx_msg->tx_msgid = device->curr_msgid;
-	mutex_unlock(&device->tx_msg_lock);
+	spin_unlock_irqrestore(&device->tx_msg_lock, flags);
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
@@ -242,6 +244,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	int msg_found = 0;
 	struct acpi_ipmi_msg *tx_msg;
 	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
 		dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
@@ -250,7 +253,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		ipmi_free_recv_msg(msg);
 		return;
 	}
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
 		if (msg->msgid == tx_msg->tx_msgid) {
 			msg_found = 1;
@@ -258,7 +261,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		}
 	}
 
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
 			"returned.\n", msg->msgid);
@@ -378,6 +381,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	struct acpi_ipmi_device *ipmi_device = handler_context;
 	int err, rem_time;
 	acpi_status status;
+	unsigned long flags;
 	/*
 	 * IPMI opregion message.
 	 * IPMI message is firstly written to the BMC and system software
@@ -395,9 +399,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 		return AE_NO_MEMORY;
 
 	acpi_format_ipmi_msg(tx_msg, address, value);
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	err = ipmi_request_settime(ipmi_device->user_interface,
 					&tx_msg->addr,
 					tx_msg->tx_msgid,
@@ -413,9 +417,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	status = AE_OK;
 
 end_label:
-	mutex_lock(&ipmi_device->tx_msg_lock);
+	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_del(&tx_msg->head);
-	mutex_unlock(&ipmi_device->tx_msg_lock);
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	kfree(tx_msg);
 	return status;
 }
@@ -457,7 +461,7 @@ static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
 
 	INIT_LIST_HEAD(&ipmi_device->head);
 
-	mutex_init(&ipmi_device->tx_msg_lock);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
 	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
 	ipmi_install_space_handler(ipmi_device);
 
Signature · 1 line
1.7.10
author
#49 lore raw

PATCH v2 2/12 ACPI/IPMI: Fix potential response buffer overflow

In reply to Lv Zheng

This patch enhances sanity checks on message size to avoid potential buffer overflow.

The kernel IPMI message size is IPMI_MAX_MSG_LENGTH(272 bytes) while the ACPI specification defined IPMI message size is 64 bytes. The difference is not handled by the original codes. This may cause crash in the response handling codes.
This patch fixes this gap and also combines rx_data/tx_data to use single data/len pair since they need not be seperated.

---
 drivers/acpi/acpi_ipmi.c |   53 +++++++++++++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 20 deletions(-)
drivers/acpi/acpi_ipmi.c +33 -20
index a6977e1..7397135 100644
@@ -52,6 +52,7 @@ MODULE_LICENSE("GPL");
 #define ACPI_IPMI_UNKNOWN		0x07
 /* the IPMI timeout is 5s */
 #define IPMI_TIMEOUT			(5 * HZ)
+#define ACPI_IPMI_MAX_MSG_LENGTH	64
 
 struct acpi_ipmi_device {
 	/* the device list attached to driver_data.ipmi_devices */
@@ -90,11 +91,9 @@ struct acpi_ipmi_msg {
 	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;
+	/* tx/rx data . And copy it from/to ACPI object buffer */
+	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
+	u8	rx_len;
 	struct acpi_ipmi_device *device;
 };
 
@@ -102,7 +101,7 @@ struct acpi_ipmi_msg {
 struct acpi_ipmi_buffer {
 	u8 status;
 	u8 length;
-	u8 data[64];
+	u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
 };
 
 static void ipmi_register_bmc(int iface, struct device *dev);
@@ -141,7 +140,7 @@ static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 
 #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,
+static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 				acpi_physical_address address,
 				acpi_integer *value)
 {
@@ -157,15 +156,21 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 	 */
 	msg->netfn = IPMI_OP_RGN_NETFN(address);
 	msg->cmd = IPMI_OP_RGN_CMD(address);
-	msg->data = tx_msg->tx_data;
+	msg->data = tx_msg->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 */
+	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
+		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+			      "Unexpected request (msg len %d).\n",
+			      buffer->length);
+		return -EINVAL;
+	}
 	msg->data_len = buffer->length;
-	memcpy(tx_msg->tx_data, buffer->data, msg->data_len);
+	memcpy(tx_msg->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,
@@ -183,6 +188,7 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 	device->curr_msgid++;
 	tx_msg->tx_msgid = device->curr_msgid;
 	spin_unlock_irqrestore(&device->tx_msg_lock, flags);
+	return 0;
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
@@ -214,7 +220,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 	 */
 	buffer->status = ACPI_IPMI_OK;
 	buffer->length = msg->rx_len;
-	memcpy(buffer->data, msg->rx_data, msg->rx_len);
+	memcpy(buffer->data, msg->data, msg->rx_len);
 }
 
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
@@ -250,8 +256,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
 			"returned user %p, expected user %p\n",
 			msg->user, ipmi_device->user_interface);
-		ipmi_free_recv_msg(msg);
-		return;
+		goto out_msg;
 	}
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
@@ -265,17 +270,21 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
 			"returned.\n", msg->msgid);
-		ipmi_free_recv_msg(msg);
-		return;
+		goto out_msg;
 	}
 
-	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);
+	/* copy the response data to Rx_data buffer */
+	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
+		dev_WARN_ONCE(&pnp_dev->dev, true,
+			      "Unexpected response (msg len %d).\n",
+			      msg->msg.data_len);
+	} else {
 		tx_msg->rx_len = msg->msg.data_len;
+		memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
 		tx_msg->msg_done = 1;
 	}
 	complete(&tx_msg->tx_complete);
+out_msg:
 	ipmi_free_recv_msg(msg);
 };
 
@@ -398,7 +407,10 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if (!tx_msg)
 		return AE_NO_MEMORY;
 
-	acpi_format_ipmi_msg(tx_msg, address, value);
+	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
+		status = AE_TYPE;
+		goto out_msg;
+	}
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -409,17 +421,18 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 					NULL, 0, 0, 0);
 	if (err) {
 		status = AE_ERROR;
-		goto end_label;
+		goto out_list;
 	}
 	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
 					IPMI_TIMEOUT);
 	acpi_format_ipmi_response(tx_msg, value, rem_time);
 	status = AE_OK;
 
-end_label:
+out_list:
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_del(&tx_msg->head);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+out_msg:
 	kfree(tx_msg);
 	return status;
 }
Signature · 1 line
1.7.10
author
#50 lore raw

PATCH v2 3/12 ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers

In reply to Lv Zheng

This patch fixes races caused by unprotected ACPI IPMI transfers.

We can see the following crashes may occur:
1. There is no tx_msg_lock held for iterating tx_msg_list in ipmi_flush_tx_msg() while it is parellel unlinked on failure in acpi_ipmi_space_handler() under protection of tx_msg_lock.
2. There is no lock held for freeing tx_msg in acpi_ipmi_space_handler() while it is parellel accessed in ipmi_flush_tx_msg() and ipmi_msg_handler().

This patch enhances tx_msg_lock to protect all tx_msg accesses to solve this issue. Then tx_msg_lock is always held around complete() and tx_msg accesses.

---
 drivers/acpi/acpi_ipmi.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/acpi/acpi_ipmi.c +6 -2
index 7397135..87307ba 100644
@@ -228,11 +228,14 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	struct acpi_ipmi_msg *tx_msg, *temp;
 	int count = HZ / 10;
 	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
 
 	/* wait for about 100ms to flush the tx message list */
 	while (count--) {
@@ -266,11 +269,10 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		}
 	}
 
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
 			"returned.\n", msg->msgid);
-		goto out_msg;
+		goto out_lock;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -284,6 +286,8 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		tx_msg->msg_done = 1;
 	}
 	complete(&tx_msg->tx_complete);
+out_lock:
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	ipmi_free_recv_msg(msg);
 };
Signature · 1 line
1.7.10
author
#51 lore raw

PATCH v2 4/12 ACPI/IPMI: Fix race caused by the timed out ACPI IPMI transfers

In reply to Lv Zheng

This patch fixes races caused by timed out ACPI IPMI transfers.

This patch uses timeout mechanism provided by ipmi_si to avoid the race that the msg_done flag is set but without any protection, its content can be invalid. Thanks for the suggestion of Corey Minyard.

---
 drivers/acpi/acpi_ipmi.c |   49 +++++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 22 deletions(-)
drivers/acpi/acpi_ipmi.c +27 -22
index 87307ba..9171a1a 100644
@@ -51,7 +51,7 @@ MODULE_LICENSE("GPL");
 #define ACPI_IPMI_TIMEOUT		0x10
 #define ACPI_IPMI_UNKNOWN		0x07
 /* the IPMI timeout is 5s */
-#define IPMI_TIMEOUT			(5 * HZ)
+#define IPMI_TIMEOUT			(5000)
 #define ACPI_IPMI_MAX_MSG_LENGTH	64
 
 struct acpi_ipmi_device {
@@ -135,6 +135,7 @@ static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
+	ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN;
 	return ipmi_msg;
 }
 
@@ -192,7 +193,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
-		acpi_integer *value, int rem_time)
+		acpi_integer *value)
 {
 	struct acpi_ipmi_buffer *buffer;
 
@@ -201,24 +202,17 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 	 * IPMI message returned by IPMI command.
 	 */
 	buffer = (struct acpi_ipmi_buffer *)value;
-	if (!rem_time && !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 the flag of msg_done is not set, it means that the IPMI command is
+	 * not executed correctly.
 	 */
-	if (!msg->msg_done || !msg->rx_len) {
-		buffer->status = ACPI_IPMI_UNKNOWN;
+	buffer->status = msg->msg_done;
+	if (msg->msg_done != ACPI_IPMI_OK)
 		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->data, msg->rx_len);
 }
@@ -280,11 +274,23 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		dev_WARN_ONCE(&pnp_dev->dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
-	} else {
-		tx_msg->rx_len = msg->msg.data_len;
-		memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
-		tx_msg->msg_done = 1;
+		goto out_comp;
 	}
+	/* response msg is an error msg */
+	msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
+	if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
+	    msg->msg.data_len == 1) {
+		if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) {
+			dev_WARN_ONCE(&pnp_dev->dev, true,
+				      "Unexpected response (timeout).\n");
+			tx_msg->msg_done = ACPI_IPMI_TIMEOUT;
+		}
+		goto out_comp;
+	}
+	tx_msg->rx_len = msg->msg.data_len;
+	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+	tx_msg->msg_done = ACPI_IPMI_OK;
+out_comp:
 	complete(&tx_msg->tx_complete);
 out_lock:
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -392,7 +398,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 {
 	struct acpi_ipmi_msg *tx_msg;
 	struct acpi_ipmi_device *ipmi_device = handler_context;
-	int err, rem_time;
+	int err;
 	acpi_status status;
 	unsigned long flags;
 	/*
@@ -422,14 +428,13 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 					&tx_msg->addr,
 					tx_msg->tx_msgid,
 					&tx_msg->tx_message,
-					NULL, 0, 0, 0);
+					NULL, 0, 0, IPMI_TIMEOUT);
 	if (err) {
 		status = AE_ERROR;
 		goto out_list;
 	}
-	rem_time = wait_for_completion_timeout(&tx_msg->tx_complete,
-					IPMI_TIMEOUT);
-	acpi_format_ipmi_response(tx_msg, value, rem_time);
+	wait_for_completion(&tx_msg->tx_complete);
+	acpi_format_ipmi_response(tx_msg, value);
 	status = AE_OK;
 
 out_list:
Signature · 1 line
1.7.10
author
#52 lore raw

PATCH v2 5/12 ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user

In reply to Lv Zheng

This patch uses reference counting to fix the race caused by the unprotected ACPI IPMI user.

There are two rules for using ipmi_si APIs:
1. In ipmi_si, ipmi_destroy_user() can ensure no ipmi_recv_msg be passed to ipmi_msg_handler(), but ipmi_request_settime() can not use a non-valid ipmi_user_t. This means the ipmi_si users must ensure no local references on ipmi_user_t before invoking ipmi_destroy_user().
2. In ipmi_si, smi_gone()/new_smi() callbacks are protected by smi_watchers_mutex, thus their invocations are serialized. But as a new smi can re-use the freed intf_num, it requires that the callback implementation must not use intf_num as an identification mean or it must ensure all references to the previous smi are all dropped before exiting smi_gone() callback.

As the acpi_ipmi_device->user_interface check in acpi_ipmi_space_handler() can happen before setting user_interface to NULL and codes after the check in acpi_ipmi_space_handler() can happen after user_interface becoming NULL, then the on-going acpi_ipmi_space_handler() still can pass an invalid acpi_ipmi_device->user_interface to ipmi_request_settime(). Such race condition is not allowed by the IPMI layer's API design as crash will happen in ipmi_request_settime().

This patch follows ipmi_devintf.c design:
1. Invoking ipmi_destroy_user() after the reference count of acpi_ipmi_device dropping to 0. References of acpi_ipmi_device dropping to 0 also means tx_msg related to this acpi_ipmi_device are all freed. This matches IPMI layer's API calling rule on ipmi_destroy_user() and ipmi_request_settime().
2. ipmi_flush_tx_msg() is performed so that no on-going tx_msg can still be running in acpi_ipmi_space_handler(). And it is invoked after invoking __ipmi_dev_kill() where acpi_ipmi_device is deleted from the list with a "dead" flag set, and the "dead" flag check is also introduced to the point where a tx_msg is going to be added to the tx_msg_list so that no new tx_msg can be created after returning from the __ipmi_dev_kill().
3. The waiting codes in ipmi_flush_tx_msg() is deleted because it is not required since this patch ensures no acpi_ipmi reference is still held for ipmi_user_t before calling ipmi_destroy_user() and ipmi_destroy_user() can ensure no more ipmi_msg_handler() can happen after returning from ipmi_destroy_user().
4. The flushing of tx_msg is also moved out of ipmi_lock in this patch.

The forthcoming IPMI operation region handler installation changes also requires acpi_ipmi_device be handled in this style.

Authorship is also updated due to this design change.

---
 drivers/acpi/acpi_ipmi.c |  249 +++++++++++++++++++++++++++++-----------------
 1 file changed, 156 insertions(+), 93 deletions(-)
drivers/acpi/acpi_ipmi.c +156 -93
index 9171a1a..b285386 100644
@@ -1,8 +1,9 @@
 /*
  *  acpi_ipmi.c - ACPI IPMI opregion
  *
- *  Copyright (C) 2010 Intel Corporation
- *  Copyright (C) 2010 Zhao Yakui <[email protected]>
+ *  Copyright (C) 2010, 2013 Intel Corporation
+ *    Author: Zhao Yakui <[email protected]>
+ *            Lv Zheng <[email protected]>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
@@ -67,6 +68,8 @@ struct acpi_ipmi_device {
 	long curr_msgid;
 	unsigned long flags;
 	struct ipmi_smi_info smi_data;
+	bool dead;
+	struct kref kref;
 };
 
 struct ipmi_driver_data {
@@ -107,8 +110,8 @@ struct acpi_ipmi_buffer {
 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 void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device);
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device);
+static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
+static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -122,6 +125,88 @@ static struct ipmi_driver_data driver_data = {
 	},
 };
 
+static struct acpi_ipmi_device *
+ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+{
+	struct acpi_ipmi_device *ipmi_device;
+	int err;
+	ipmi_user_t user;
+
+	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+	if (!ipmi_device)
+		return NULL;
+
+	kref_init(&ipmi_device->kref);
+	INIT_LIST_HEAD(&ipmi_device->head);
+	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+	spin_lock_init(&ipmi_device->tx_msg_lock);
+
+	ipmi_device->handle = handle;
+	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
+	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->ipmi_ifnum = iface;
+
+	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+			       ipmi_device, &user);
+	if (err) {
+		put_device(smi_data->dev);
+		kfree(ipmi_device);
+		return NULL;
+	}
+	ipmi_device->user_interface = user;
+	ipmi_install_space_handler(ipmi_device);
+
+	return ipmi_device;
+}
+
+static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
+{
+	ipmi_remove_space_handler(ipmi_device);
+	ipmi_destroy_user(ipmi_device->user_interface);
+	put_device(ipmi_device->smi_data.dev);
+	kfree(ipmi_device);
+}
+
+static void ipmi_dev_release_kref(struct kref *kref)
+{
+	struct acpi_ipmi_device *ipmi =
+		container_of(kref, struct acpi_ipmi_device, kref);
+
+	ipmi_dev_release(ipmi);
+}
+
+static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
+{
+	list_del(&ipmi_device->head);
+	/*
+	 * Always setting dead flag after deleting from the list or
+	 * list_for_each_entry() codes must get changed.
+	 */
+	ipmi_device->dead = true;
+}
+
+static struct acpi_ipmi_device *acpi_ipmi_dev_get(int iface)
+{
+	struct acpi_ipmi_device *temp, *ipmi_device = NULL;
+
+	mutex_lock(&driver_data.ipmi_lock);
+	list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
+		if (temp->ipmi_ifnum == iface) {
+			ipmi_device = temp;
+			kref_get(&ipmi_device->kref);
+			break;
+		}
+	}
+	mutex_unlock(&driver_data.ipmi_lock);
+
+	return ipmi_device;
+}
+
+static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)
+{
+	kref_put(&ipmi_device->kref, ipmi_dev_release_kref);
+}
+
 static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *ipmi_msg;
@@ -220,25 +305,22 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
 	struct acpi_ipmi_msg *tx_msg, *temp;
-	int count = HZ / 10;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 	unsigned long flags;
 
+	/*
+	 * NOTE: On-going ipmi_recv_msg
+	 * ipmi_msg_handler() may still be invoked by ipmi_si after
+	 * flushing.  But it is safe to do a fast flushing on module_exit()
+	 * without waiting for all ipmi_recv_msg(s) to complete from
+	 * ipmi_msg_handler() as it is ensured by ipmi_si that all
+	 * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user().
+	 */
 	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
 	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);
 	}
 	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
-
-	/* wait for about 100ms 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)
@@ -302,7 +384,6 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
 	struct pnp_dev *pnp_dev;
-	ipmi_user_t		user;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -312,12 +393,18 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 	if (err)
 		return;
 
-	if (smi_data.addr_src != SI_ACPI) {
-		put_device(smi_data.dev);
-		return;
-	}
-
+	if (smi_data.addr_src != SI_ACPI)
+		goto err_ref;
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
+	if (!handle)
+		goto err_ref;
+	pnp_dev = to_pnp_dev(smi_data.dev);
+
+	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	if (!ipmi_device) {
+		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		goto err_ref;
+	}
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
@@ -326,34 +413,18 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 		 * to the device list, don't add it again.
 		 */
 		if (temp->handle == handle)
-			goto out;
+			goto err_lock;
 	}
 
-	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
-
-	if (!ipmi_device)
-		goto out;
-
-	pnp_dev = to_pnp_dev(smi_data.dev);
-	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = pnp_dev;
-
-	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
-					ipmi_device, &user);
-	if (err) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
-		kfree(ipmi_device);
-		goto out;
-	}
-	acpi_add_ipmi_device(ipmi_device);
-	ipmi_device->user_interface = user;
-	ipmi_device->ipmi_ifnum = iface;
+	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
-	memcpy(&ipmi_device->smi_data, &smi_data, sizeof(struct ipmi_smi_info));
+	put_device(smi_data.dev);
 	return;
 
-out:
+err_lock:
 	mutex_unlock(&driver_data.ipmi_lock);
+	ipmi_dev_release(ipmi_device);
+err_ref:
 	put_device(smi_data.dev);
 	return;
 }
@@ -361,19 +432,22 @@ out:
 static void ipmi_bmc_gone(int iface)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
+	bool dev_found = false;
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry_safe(ipmi_device, temp,
 				&driver_data.ipmi_devices, head) {
-		if (ipmi_device->ipmi_ifnum != iface)
-			continue;
-
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
-		break;
+		if (ipmi_device->ipmi_ifnum != iface) {
+			dev_found = true;
+			__ipmi_dev_kill(ipmi_device);
+			break;
+		}
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
+	if (dev_found) {
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+	}
 }
 /* --------------------------------------------------------------------------
  *			Address Space Management
@@ -397,7 +471,8 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 		      void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
-	struct acpi_ipmi_device *ipmi_device = handler_context;
+	int iface = (long)handler_context;
+	struct acpi_ipmi_device *ipmi_device;
 	int err;
 	acpi_status status;
 	unsigned long flags;
@@ -410,20 +485,31 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	if (!ipmi_device->user_interface)
+	ipmi_device = acpi_ipmi_dev_get(iface);
+	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
 	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg)
-		return AE_NO_MEMORY;
+	if (!tx_msg) {
+		status = AE_NO_MEMORY;
+		goto out_ref;
+	}
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
 		status = AE_TYPE;
 		goto out_msg;
 	}
+	mutex_lock(&driver_data.ipmi_lock);
+	/* Do not add a tx_msg that can not be flushed. */
+	if (ipmi_device->dead) {
+		status = AE_NOT_EXIST;
+		mutex_unlock(&driver_data.ipmi_lock);
+		goto out_msg;
+	}
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+	mutex_unlock(&driver_data.ipmi_lock);
 	err = ipmi_request_settime(ipmi_device->user_interface,
 					&tx_msg->addr,
 					tx_msg->tx_msgid,
@@ -443,6 +529,8 @@ out_list:
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
 	kfree(tx_msg);
+out_ref:
+	acpi_ipmi_dev_put(ipmi_device);
 	return status;
 }
 
@@ -465,9 +553,8 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 		return 0;
 
 	status = acpi_install_address_space_handler(ipmi->handle,
-						    ACPI_ADR_SPACE_IPMI,
-						    &acpi_ipmi_space_handler,
-						    NULL, ipmi);
+				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler,
+				NULL, (void *)((long)ipmi->ipmi_ifnum));
 	if (ACPI_FAILURE(status)) {
 		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
@@ -478,36 +565,6 @@ static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
 	return 0;
 }
 
-static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-
-	INIT_LIST_HEAD(&ipmi_device->head);
-
-	spin_lock_init(&ipmi_device->tx_msg_lock);
-	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
-	ipmi_install_space_handler(ipmi_device);
-
-	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
-}
-
-static void acpi_remove_ipmi_device(struct acpi_ipmi_device *ipmi_device)
-{
-	/*
-	 * If the IPMI user interface is created, it should be
-	 * destroyed.
-	 */
-	if (ipmi_device->user_interface) {
-		ipmi_destroy_user(ipmi_device->user_interface);
-		ipmi_device->user_interface = NULL;
-	}
-	/* flush the Tx_msg list */
-	if (!list_empty(&ipmi_device->tx_msg_list))
-		ipmi_flush_tx_msg(ipmi_device);
-
-	list_del(&ipmi_device->head);
-	ipmi_remove_space_handler(ipmi_device);
-}
-
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
@@ -524,7 +581,7 @@ static int __init acpi_ipmi_init(void)
 
 static void __exit acpi_ipmi_exit(void)
 {
-	struct acpi_ipmi_device *ipmi_device, *temp;
+	struct acpi_ipmi_device *ipmi_device;
 
 	if (acpi_disabled)
 		return;
@@ -538,11 +595,17 @@ static void __exit acpi_ipmi_exit(void)
 	 * handler and free it.
 	 */
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
-		acpi_remove_ipmi_device(ipmi_device);
-		put_device(ipmi_device->smi_data.dev);
-		kfree(ipmi_device);
+	while (!list_empty(&driver_data.ipmi_devices)) {
+		ipmi_device = list_first_entry(&driver_data.ipmi_devices,
+					       struct acpi_ipmi_device,
+					       head);
+		__ipmi_dev_kill(ipmi_device);
+		mutex_unlock(&driver_data.ipmi_lock);
+
+		ipmi_flush_tx_msg(ipmi_device);
+		acpi_ipmi_dev_put(ipmi_device);
+
+		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 }
Signature · 1 line
1.7.10
author
#53 lore raw

PATCH v2 6/12 ACPI/IPMI: Fix issue caused by the per-device registration of the IPMI operation region handler

In reply to Lv Zheng

It is found on a real machine, in its ACPI namespace, the IPMI OperationRegions (in the ACPI000D - ACPI power meter) are not defined under the IPMI system interface device (the IPI0001 with KCS type returned from _IFT control method):
Device (PMI0)

  {
      Name (_HID, "ACPI000D")  // _HID: Hardware ID
      OperationRegion (SYSI, IPMI, 0x0600, 0x0100)
      Field (SYSI, BufferAcc, Lock, Preserve)
      {
          AccessAs (BufferAcc, 0x01),
          Offset (0x58),
          SCMD,   8,
          GCMD,   8
      }

      OperationRegion (POWR, IPMI, 0x3000, 0x0100)
      Field (POWR, BufferAcc, Lock, Preserve)
      {
          AccessAs (BufferAcc, 0x01),
          Offset (0xB3),
          GPMM,   8
      }

}

Device (PCI0)

  {
      Device (ISA)
      {
          Device (NIPM)
          {
              Name (_HID, EisaId ("IPI0001"))  // _HID: Hardware ID
              Method (_IFT, 0, NotSerialized)  // _IFT: IPMI Interface Type
              {
                  Return (0x01)
              }
          }
      }

}
Current ACPI_IPMI code registers IPMI operation region handler on a per-device basis, so that for above namespace, the IPMI operation region handler is registered only under the scope of \_SB.PCI0.ISA.NIPM. Thus when an IPMI operation region field of \PMI0 is accessed, there are errors reported on such platform:
ACPI Error: No handlers for Region [IPMI]
ACPI Error: Region IPMI(7) has no handler
The solution is to install IPMI operation region handler from root node so that every object that defines IPMI OperationRegion can get an address space handler registered.

When an IPMI operation region field is accessed, the Network Function (0x06 for SYSI and 0x30 for POWR) and the Command (SCMD, GCMD, GPMM) are passed to the operation region handler, there is no system interface specified by the BIOS. The patch tries to select one system interface by monitoring the system interface notification. IPMI messages passed from the ACPI codes are sent to this selected global IPMI system interface.

Known issues:
- How to select the IPMI system interface:
Currently, the ACPI_IPMI always selects the first registered one with the ACPI handle set (i.e., defined in the ACPI namespace). It's hard to determine the selection when there are multiple IPMI system interfaces defined in the ACPI namespace.
According to the IPMI specification:
A BMC device may make available multiple system interfaces, but only one management controller is allowed to be 'active' BMC that provides BMC functionality for the system (in case of a 'partitioned' system, there can be only one active BMC per partition). Only the system interface(s) for the active BMC allowed to respond to the 'Get Device Id' command. According to the ipmi_si desigin:
The ipmi_si registeration notifications can only happen after a successful "Get Device ID" command.
Thus it should be OK for non-partitioned systems to do such selection. But we do not have too much knowledges on 'partitioned' systems.

---
 drivers/acpi/acpi_ipmi.c |   81 +++++++++++++++++++---------------------------
 1 file changed, 34 insertions(+), 47 deletions(-)
drivers/acpi/acpi_ipmi.c +34 -47
index b285386..7ec4cd1 100644
@@ -46,7 +46,6 @@ 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
@@ -66,7 +65,6 @@ struct acpi_ipmi_device {
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	unsigned long flags;
 	struct ipmi_smi_info smi_data;
 	bool dead;
 	struct kref kref;
@@ -77,6 +75,14 @@ struct ipmi_driver_data {
 	struct ipmi_smi_watcher	bmc_events;
 	struct ipmi_user_hndl	ipmi_hndlrs;
 	struct mutex		ipmi_lock;
+	/*
+	 * NOTE: IPMI System Interface Selection
+	 * There is no system interface specified by the IPMI operation
+	 * region access.  We try to select one system interface with ACPI
+	 * handle set.  IPMI messages passed from the ACPI codes are sent
+	 * to this selected global IPMI system interface.
+	 */
+	struct acpi_ipmi_device *selected_smi;
 };
 
 struct acpi_ipmi_msg {
@@ -110,8 +116,6 @@ struct acpi_ipmi_buffer {
 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 int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
-static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
 
 static struct ipmi_driver_data driver_data = {
 	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -154,14 +158,12 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 		return NULL;
 	}
 	ipmi_device->user_interface = user;
-	ipmi_install_space_handler(ipmi_device);
 
 	return ipmi_device;
 }
 
 static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
 {
-	ipmi_remove_space_handler(ipmi_device);
 	ipmi_destroy_user(ipmi_device->user_interface);
 	put_device(ipmi_device->smi_data.dev);
 	kfree(ipmi_device);
@@ -178,6 +180,8 @@ static void ipmi_dev_release_kref(struct kref *kref)
 static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
 {
 	list_del(&ipmi_device->head);
+	if (driver_data.selected_smi == ipmi_device)
+		driver_data.selected_smi = NULL;
 	/*
 	 * Always setting dead flag after deleting from the list or
 	 * list_for_each_entry() codes must get changed.
@@ -185,17 +189,14 @@ static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
 	ipmi_device->dead = true;
 }
 
-static struct acpi_ipmi_device *acpi_ipmi_dev_get(int iface)
+static struct acpi_ipmi_device *acpi_ipmi_dev_get(void)
 {
-	struct acpi_ipmi_device *temp, *ipmi_device = NULL;
+	struct acpi_ipmi_device *ipmi_device = NULL;
 
 	mutex_lock(&driver_data.ipmi_lock);
-	list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
-		if (temp->ipmi_ifnum == iface) {
-			ipmi_device = temp;
-			kref_get(&ipmi_device->kref);
-			break;
-		}
+	if (driver_data.selected_smi) {
+		ipmi_device = driver_data.selected_smi;
+		kref_get(&ipmi_device->kref);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 
@@ -416,6 +417,8 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 			goto err_lock;
 	}
 
+	if (!driver_data.selected_smi)
+		driver_data.selected_smi = ipmi_device;
 	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
 	put_device(smi_data.dev);
@@ -443,6 +446,10 @@ static void ipmi_bmc_gone(int iface)
 			break;
 		}
 	}
+	if (!driver_data.selected_smi)
+		driver_data.selected_smi = list_first_entry_or_null(
+					&driver_data.ipmi_devices,
+					struct acpi_ipmi_device, head);
 	mutex_unlock(&driver_data.ipmi_lock);
 	if (dev_found) {
 		ipmi_flush_tx_msg(ipmi_device);
@@ -471,7 +478,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 		      void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
-	int iface = (long)handler_context;
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
 	acpi_status status;
@@ -485,7 +491,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_dev_get(iface);
+	ipmi_device = acpi_ipmi_dev_get();
 	if (!ipmi_device)
 		return AE_NOT_EXIST;
 
@@ -534,47 +540,26 @@ out_ref:
 	return status;
 }
 
-static void ipmi_remove_space_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_space_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, (void *)((long)ipmi->ipmi_ifnum));
-	if (ACPI_FAILURE(status)) {
-		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
-		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
-			"handle\n");
-		return -EINVAL;
-	}
-	set_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
-	return 0;
-}
-
 static int __init acpi_ipmi_init(void)
 {
 	int result = 0;
+	acpi_status status;
 
 	if (acpi_disabled)
 		return result;
 
 	mutex_init(&driver_data.ipmi_lock);
 
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler,
+				NULL, NULL);
+	if (ACPI_FAILURE(status)) {
+		pr_warn("Can't register IPMI opregion space handle\n");
+		return -EINVAL;
+	}
 	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
+	if (result)
+		pr_err("Can't register IPMI system interface watcher\n");
 
 	return result;
 }
@@ -608,6 +593,8 @@ static void __exit acpi_ipmi_exit(void)
 		mutex_lock(&driver_data.ipmi_lock);
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
+	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
+				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler);
 }
 
 module_init(acpi_ipmi_init);
Signature · 1 line
1.7.10
author
#54 lore raw

PATCH v2 7/12 ACPI/IPMI: Add reference counting for ACPI IPMI transfers

In reply to Lv Zheng

This patch adds reference counting for ACPI IPMI transfers to tune the locking granularity of tx_msg_lock.

This patch also makes whole acpi_ipmi module's coding style consistent by using reference counting for all its objects (i.e., acpi_ipmi_device and acpi_ipmi_msg).

The acpi_ipmi_msg handling is re-designed using referece counting.
1. tx_msg is always unlinked before complete(), so that it is safe to put complete() out side of tx_msg_lock.
2. Increasing the reference of tx_msg before calling ipmi_request_settime() and introducing tx_msg_lock protected ipmi_cancel_tx_msg() so that a complete() can happen in parellel with tx_msg unlinking in the failure cases.
3. tx_msg holds the reference of acpi_ipmi_device so that it can be flushed and freed in the contexts other than acpi_ipmi_space_handler().

The lockdep_chains shows all acpi_ipmi locks are leaf locks after the tuning:
1. ipmi_lock is always leaf:
irq_context: 0
[ffffffff81a943f8] smi_watchers_mutex [ffffffffa06eca60] driver_data.ipmi_lock
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a6678] s_active#103
[ffffffffa06eca60] driver_data.ipmi_lock
2. without this patch applied, lock used by complete() is held after holding tx_msg_lock:
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a6678] s_active#103
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
[ffffffff81e36620] &p->pi_lock
irq_context: 1
[ffffffffa06ecce8] &(&ipmi_device->tx_msg_lock)->rlock [ffffffffa06eccf0] &x->wait#25
[ffffffff81e36620] &p->pi_lock
[ffffffff81e5d0a8] &rq->lock
3. with this patch applied, tx_msg_lock is always leaf:
irq_context: 0
[ffffffff82767b40] &buffer->mutex
[ffffffffa00a66d8] s_active#107
[ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock
irq_context: 1
[ffffffffa07ecdc8] &(&ipmi_device->tx_msg_lock)->rlock

---
 drivers/acpi/acpi_ipmi.c |  117 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 85 insertions(+), 32 deletions(-)
drivers/acpi/acpi_ipmi.c +85 -32
index 7ec4cd1..b9da5ef 100644
@@ -104,6 +104,7 @@ struct acpi_ipmi_msg {
 	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
 	u8	rx_len;
 	struct acpi_ipmi_device *device;
+	struct kref	kref;
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -208,16 +209,20 @@ static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)
 	kref_put(&ipmi_device->kref, ipmi_dev_release_kref);
 }
 
-static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 {
+	struct acpi_ipmi_device *ipmi;
 	struct acpi_ipmi_msg *ipmi_msg;
-	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
 
+	ipmi = acpi_ipmi_dev_get();
+	if (!ipmi)
+		return NULL;
 	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");
+	if (!ipmi_msg) {
+		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+	kref_init(&ipmi_msg->kref);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
@@ -225,6 +230,32 @@ static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
 	return ipmi_msg;
 }
 
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+	acpi_ipmi_dev_put(tx_msg->device);
+	kfree(tx_msg);
+}
+
+static void ipmi_msg_release_kref(struct kref *kref)
+{
+	struct acpi_ipmi_msg *tx_msg =
+		container_of(kref, struct acpi_ipmi_msg, kref);
+
+	ipmi_msg_release(tx_msg);
+}
+
+static struct acpi_ipmi_msg *acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+	kref_get(&tx_msg->kref);
+
+	return tx_msg;
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+	kref_put(&tx_msg->kref, ipmi_msg_release_kref);
+}
+
 #define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
 #define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
@@ -305,7 +336,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 
 static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 {
-	struct acpi_ipmi_msg *tx_msg, *temp;
+	struct acpi_ipmi_msg *tx_msg;
 	unsigned long flags;
 
 	/*
@@ -317,18 +348,47 @@ static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
 	 * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user().
 	 */
 	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
-	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) {
+	while (!list_empty(&ipmi->tx_msg_list)) {
+		tx_msg = list_first_entry(&ipmi->tx_msg_list,
+					  struct acpi_ipmi_msg,
+					  head);
+		list_del(&tx_msg->head);
+		spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
 		/* wake up the sleep thread on the Tx msg */
 		complete(&tx_msg->tx_complete);
+		acpi_ipmi_msg_put(tx_msg);
+		spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	}
+	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+}
+
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+			       struct acpi_ipmi_msg *msg)
+{
+	struct acpi_ipmi_msg *tx_msg, *temp;
+	bool msg_found = false;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) {
+		if (msg == tx_msg) {
+			msg_found = true;
+			list_del(&tx_msg->head);
+			break;
+		}
 	}
 	spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+	if (msg_found)
+		acpi_ipmi_msg_put(tx_msg);
 }
 
 static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 {
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
-	int msg_found = 0;
-	struct acpi_ipmi_msg *tx_msg;
+	bool msg_found = false;
+	struct acpi_ipmi_msg *tx_msg, *temp;
 	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
 	unsigned long flags;
 
@@ -339,17 +399,19 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		goto out_msg;
 	}
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
-	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
+	list_for_each_entry_safe(tx_msg, temp, &ipmi_device->tx_msg_list, head) {
 		if (msg->msgid == tx_msg->tx_msgid) {
-			msg_found = 1;
+			msg_found = true;
+			list_del(&tx_msg->head);
 			break;
 		}
 	}
+	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
 		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
 			"returned.\n", msg->msgid);
-		goto out_lock;
+		goto out_msg;
 	}
 
 	/* copy the response data to Rx_data buffer */
@@ -375,8 +437,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	tx_msg->msg_done = ACPI_IPMI_OK;
 out_comp:
 	complete(&tx_msg->tx_complete);
-out_lock:
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+	acpi_ipmi_msg_put(tx_msg);
 out_msg:
 	ipmi_free_recv_msg(msg);
 };
@@ -491,26 +552,23 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	if ((function & ACPI_IO_MASK) == ACPI_READ)
 		return AE_TYPE;
 
-	ipmi_device = acpi_ipmi_dev_get();
-	if (!ipmi_device)
+	tx_msg = ipmi_msg_alloc();
+	if (!tx_msg)
 		return AE_NOT_EXIST;
 
-	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
-	if (!tx_msg) {
-		status = AE_NO_MEMORY;
-		goto out_ref;
-	}
+	ipmi_device = tx_msg->device;
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
-		status = AE_TYPE;
-		goto out_msg;
+		ipmi_msg_release(tx_msg);
+		return AE_TYPE;
 	}
+	acpi_ipmi_msg_get(tx_msg);
 	mutex_lock(&driver_data.ipmi_lock);
 	/* Do not add a tx_msg that can not be flushed. */
 	if (ipmi_device->dead) {
-		status = AE_NOT_EXIST;
 		mutex_unlock(&driver_data.ipmi_lock);
-		goto out_msg;
+		ipmi_msg_release(tx_msg);
+		return AE_NOT_EXIST;
 	}
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
@@ -523,20 +581,15 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 					NULL, 0, 0, IPMI_TIMEOUT);
 	if (err) {
 		status = AE_ERROR;
-		goto out_list;
+		goto out_msg;
 	}
 	wait_for_completion(&tx_msg->tx_complete);
 	acpi_format_ipmi_response(tx_msg, value);
 	status = AE_OK;
 
-out_list:
-	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
-	list_del(&tx_msg->head);
-	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 out_msg:
-	kfree(tx_msg);
-out_ref:
-	acpi_ipmi_dev_put(ipmi_device);
+	ipmi_cancel_tx_msg(ipmi_device, tx_msg);
+	acpi_ipmi_msg_put(tx_msg);
 	return status;
 }
 
Signature · 1 line
1.7.10
author
#55 lore raw

PATCH v2 8/12 ACPI/IPMI: Cleanup several acpi_ipmi_device members

In reply to Lv Zheng

This is a trivial patch:
1. Deletes a member of the acpi_ipmi_device - smi_data which is not actually used.
2. Updates a member of the acpi_ipmi_device - pnp_dev which is only used by dev_warn() invocations, so changes it to struct device.

---
 drivers/acpi/acpi_ipmi.c |   30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)
drivers/acpi/acpi_ipmi.c +13 -17
index b9da5ef..90d57c8 100644
@@ -61,11 +61,10 @@ struct acpi_ipmi_device {
 	struct list_head tx_msg_list;
 	spinlock_t	tx_msg_lock;
 	acpi_handle handle;
-	struct pnp_dev *pnp_dev;
+	struct device *dev;
 	ipmi_user_t	user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
-	struct ipmi_smi_info smi_data;
 	bool dead;
 	struct kref kref;
 };
@@ -131,7 +130,7 @@ static struct ipmi_driver_data driver_data = {
 };
 
 static struct acpi_ipmi_device *
-ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
+ipmi_dev_alloc(int iface, struct device *dev, acpi_handle handle)
 {
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
@@ -147,14 +146,13 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 	spin_lock_init(&ipmi_device->tx_msg_lock);
 
 	ipmi_device->handle = handle;
-	ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev));
-	memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info));
+	ipmi_device->dev = get_device(dev);
 	ipmi_device->ipmi_ifnum = iface;
 
 	err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
 			       ipmi_device, &user);
 	if (err) {
-		put_device(smi_data->dev);
+		put_device(dev);
 		kfree(ipmi_device);
 		return NULL;
 	}
@@ -166,7 +164,7 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
 static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
 {
 	ipmi_destroy_user(ipmi_device->user_interface);
-	put_device(ipmi_device->smi_data.dev);
+	put_device(ipmi_device->dev);
 	kfree(ipmi_device);
 }
 
@@ -282,7 +280,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	buffer = (struct acpi_ipmi_buffer *)value;
 	/* copy the tx message data */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true,
+		dev_WARN_ONCE(tx_msg->device->dev, true,
 			      "Unexpected request (msg len %d).\n",
 			      buffer->length);
 		return -EINVAL;
@@ -389,11 +387,11 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	struct acpi_ipmi_device *ipmi_device = user_msg_data;
 	bool msg_found = false;
 	struct acpi_ipmi_msg *tx_msg, *temp;
-	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+	struct device *dev = ipmi_device->dev;
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
+		dev_warn(dev, "Unexpected response is returned. "
 			"returned user %p, expected user %p\n",
 			msg->user, ipmi_device->user_interface);
 		goto out_msg;
@@ -409,14 +407,14 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
+		dev_warn(dev, "Unexpected response (msg id %ld) is "
 			"returned.\n", msg->msgid);
 		goto out_msg;
 	}
 
 	/* copy the response data to Rx_data buffer */
 	if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
-		dev_WARN_ONCE(&pnp_dev->dev, true,
+		dev_WARN_ONCE(dev, true,
 			      "Unexpected response (msg len %d).\n",
 			      msg->msg.data_len);
 		goto out_comp;
@@ -426,7 +424,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
 	    msg->msg.data_len == 1) {
 		if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) {
-			dev_WARN_ONCE(&pnp_dev->dev, true,
+			dev_WARN_ONCE(dev, true,
 				      "Unexpected response (timeout).\n");
 			tx_msg->msg_done = ACPI_IPMI_TIMEOUT;
 		}
@@ -445,7 +443,6 @@ out_msg:
 static void ipmi_register_bmc(int iface, struct device *dev)
 {
 	struct acpi_ipmi_device *ipmi_device, *temp;
-	struct pnp_dev *pnp_dev;
 	int err;
 	struct ipmi_smi_info smi_data;
 	acpi_handle handle;
@@ -460,11 +457,10 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 	handle = smi_data.addr_info.acpi_info.acpi_handle;
 	if (!handle)
 		goto err_ref;
-	pnp_dev = to_pnp_dev(smi_data.dev);
 
-	ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle);
+	ipmi_device = ipmi_dev_alloc(iface, smi_data.dev, handle);
 	if (!ipmi_device) {
-		dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n");
+		dev_warn(smi_data.dev, "Can't create IPMI user interface\n");
 		goto err_ref;
 	}
 
Signature · 1 line
1.7.10
author
#56 lore raw

PATCH v2 9/12 ACPI/IPMI: Cleanup some initialization codes

In reply to Lv Zheng

This is a trivial patch.
1. Changes dynamic mutex initialization to static initialization.
2. Removes one acpi_ipmi_init() variable initialization as it is not needed.

---
 drivers/acpi/acpi_ipmi.c |    7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/acpi/acpi_ipmi.c +3 -4
index 90d57c8..f7b6598 100644
@@ -127,6 +127,7 @@ static struct ipmi_driver_data driver_data = {
 	.ipmi_hndlrs = {
 		.ipmi_recv_hndl = ipmi_msg_handler,
 	},
+	.ipmi_lock = __MUTEX_INITIALIZER(driver_data.ipmi_lock)
 };
 
 static struct acpi_ipmi_device *
@@ -591,13 +592,11 @@ out_msg:
 
 static int __init acpi_ipmi_init(void)
 {
-	int result = 0;
+	int result;
 	acpi_status status;
 
 	if (acpi_disabled)
-		return result;
-
-	mutex_init(&driver_data.ipmi_lock);
+		return 0;
 
 	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
 				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler,
Signature · 1 line
1.7.10
author
#57 lore raw

PATCH v2 10/12 ACPI/IPMI: Cleanup some inclusion codes

In reply to Lv Zheng

This is a trivial patch:
1. Deletes several useless header inclusions.
2. Kernel codes should always include <linux/acpi.h> instead of <acpi/acpi_bus.h> or <acpi/acpi_drivers.h> where many conditional declarations are handled.

---
 drivers/acpi/acpi_ipmi.c |   15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)
drivers/acpi/acpi_ipmi.c +1 -14
index f7b6598..9d187fd 100644
@@ -24,22 +24,9 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
-#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/acpi.h>
 #include <linux/ipmi.h>
-#include <linux/device.h>
-#include <linux/pnp.h>
 #include <linux/spinlock.h>
 
 MODULE_AUTHOR("Zhao Yakui");
Signature · 1 line
1.7.10
author
#58 lore raw

PATCH v2 11/12 ACPI/IPMI: Cleanup some Kconfig codes

In reply to Lv Zheng

This is a trivial patch:
1. Deletes duplicate Kconfig dependency as there is "if IPMI_HANDLER" around "IPMI_SI".

---
 drivers/acpi/Kconfig |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/acpi/Kconfig +2 -1
index 3278a21..d129869 100644
@@ -181,9 +181,10 @@ 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 IPMI_SI && IPMI_HANDLER
+	depends on IPMI_SI
 	default n
 	help
 	  This driver enables the ACPI to access the BMC controller. And it
Signature · 9 lines
1.7.10


------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
author
#59 lore raw

PATCH v2 12/12 ACPI/IPMI: Cleanup coding styles

In reply to Lv Zheng

This patch only introduces indentation cleanups. No functional changes.

---
 drivers/acpi/acpi_ipmi.c |  105 ++++++++++++++++++++++++++++------------------
 1 file changed, 65 insertions(+), 40 deletions(-)
drivers/acpi/acpi_ipmi.c +65 -40
index 9d187fd..ac0f52f 100644
@@ -33,7 +33,6 @@ MODULE_AUTHOR("Zhao Yakui");
 MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
 MODULE_LICENSE("GPL");
 
-
 #define ACPI_IPMI_OK			0
 #define ACPI_IPMI_TIMEOUT		0x10
 #define ACPI_IPMI_UNKNOWN		0x07
@@ -44,12 +43,14 @@ MODULE_LICENSE("GPL");
 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;
-	spinlock_t	tx_msg_lock;
+
+	spinlock_t tx_msg_lock;
 	acpi_handle handle;
 	struct device *dev;
-	ipmi_user_t	user_interface;
+	ipmi_user_t user_interface;
 	int ipmi_ifnum; /* IPMI interface number */
 	long curr_msgid;
 	bool dead;
@@ -57,10 +58,11 @@ struct acpi_ipmi_device {
 };
 
 struct ipmi_driver_data {
-	struct list_head	ipmi_devices;
-	struct ipmi_smi_watcher	bmc_events;
-	struct ipmi_user_hndl	ipmi_hndlrs;
-	struct mutex		ipmi_lock;
+	struct list_head ipmi_devices;
+	struct ipmi_smi_watcher bmc_events;
+	struct ipmi_user_hndl ipmi_hndlrs;
+	struct mutex ipmi_lock;
+
 	/*
 	 * NOTE: IPMI System Interface Selection
 	 * There is no system interface specified by the IPMI operation
@@ -73,6 +75,7 @@ struct ipmi_driver_data {
 
 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.
@@ -82,15 +85,19 @@ struct acpi_ipmi_msg {
 	 */
 	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;
+	int msg_done;
+
 	/* tx/rx data . And copy it from/to ACPI object buffer */
-	u8	data[ACPI_IPMI_MAX_MSG_LENGTH];
-	u8	rx_len;
+	u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
+	u8 rx_len;
+
 	struct acpi_ipmi_device *device;
-	struct kref	kref;
+	struct kref kref;
 };
 
 /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
@@ -132,7 +139,6 @@ ipmi_dev_alloc(int iface, struct device *dev, acpi_handle handle)
 	INIT_LIST_HEAD(&ipmi_device->head);
 	INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
 	spin_lock_init(&ipmi_device->tx_msg_lock);
-
 	ipmi_device->handle = handle;
 	ipmi_device->dev = get_device(dev);
 	ipmi_device->ipmi_ifnum = iface;
@@ -169,6 +175,7 @@ static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
 	list_del(&ipmi_device->head);
 	if (driver_data.selected_smi == ipmi_device)
 		driver_data.selected_smi = NULL;
+
 	/*
 	 * Always setting dead flag after deleting from the list or
 	 * list_for_each_entry() codes must get changed.
@@ -203,16 +210,19 @@ static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
 	ipmi = acpi_ipmi_dev_get();
 	if (!ipmi)
 		return NULL;
+
 	ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL);
 	if (!ipmi_msg) {
 		acpi_ipmi_dev_put(ipmi);
 		return NULL;
 	}
+
 	kref_init(&ipmi_msg->kref);
 	init_completion(&ipmi_msg->tx_complete);
 	INIT_LIST_HEAD(&ipmi_msg->head);
 	ipmi_msg->device = ipmi;
 	ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN;
+
 	return ipmi_msg;
 }
 
@@ -242,11 +252,11 @@ static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
 	kref_put(&tx_msg->kref, ipmi_msg_release_kref);
 }
 
-#define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
-#define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
+#define IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
+#define IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
 static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
-				acpi_physical_address address,
-				acpi_integer *value)
+				    acpi_physical_address address,
+				    acpi_integer *value)
 {
 	struct kernel_ipmi_msg *msg;
 	struct acpi_ipmi_buffer *buffer;
@@ -254,6 +264,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	unsigned long flags;
 
 	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.
@@ -261,11 +272,13 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	msg->netfn = IPMI_OP_RGN_NETFN(address);
 	msg->cmd = IPMI_OP_RGN_CMD(address);
 	msg->data = tx_msg->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 */
 	if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
 		dev_WARN_ONCE(tx_msg->device->dev, true,
@@ -275,6 +288,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 	}
 	msg->data_len = buffer->length;
 	memcpy(tx_msg->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,
@@ -288,15 +302,17 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
 
 	/* Get the msgid */
 	device = tx_msg->device;
+
 	spin_lock_irqsave(&device->tx_msg_lock, flags);
 	device->curr_msgid++;
 	tx_msg->tx_msgid = device->curr_msgid;
 	spin_unlock_irqrestore(&device->tx_msg_lock, flags);
+
 	return 0;
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
-		acpi_integer *value)
+				      acpi_integer *value)
 {
 	struct acpi_ipmi_buffer *buffer;
 
@@ -305,6 +321,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 	 * IPMI message returned by IPMI command.
 	 */
 	buffer = (struct acpi_ipmi_buffer *)value;
+
 	/*
 	 * If the flag of msg_done is not set, it means that the IPMI command is
 	 * not executed correctly.
@@ -312,6 +329,7 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
 	buffer->status = msg->msg_done;
 	if (msg->msg_done != ACPI_IPMI_OK)
 		return;
+
 	/*
 	 * If the IPMI response message is obtained correctly, the status code
 	 * will be ACPI_IPMI_OK
@@ -379,11 +397,12 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	unsigned long flags;
 
 	if (msg->user != ipmi_device->user_interface) {
-		dev_warn(dev, "Unexpected response is returned. "
-			"returned user %p, expected user %p\n",
-			msg->user, ipmi_device->user_interface);
+		dev_warn(dev,
+			 "Unexpected response is returned. returned user %p, expected user %p\n",
+			 msg->user, ipmi_device->user_interface);
 		goto out_msg;
 	}
+
 	spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
 	list_for_each_entry_safe(tx_msg, temp, &ipmi_device->tx_msg_list, head) {
 		if (msg->msgid == tx_msg->tx_msgid) {
@@ -395,8 +414,9 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 
 	if (!msg_found) {
-		dev_warn(dev, "Unexpected response (msg id %ld) is "
-			"returned.\n", msg->msgid);
+		dev_warn(dev,
+			 "Unexpected response (msg id %ld) is returned.\n",
+			 msg->msgid);
 		goto out_msg;
 	}
 
@@ -407,6 +427,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 			      msg->msg.data_len);
 		goto out_comp;
 	}
+
 	/* response msg is an error msg */
 	msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
 	if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
@@ -418,15 +439,17 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
 		}
 		goto out_comp;
 	}
+
 	tx_msg->rx_len = msg->msg.data_len;
 	memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
 	tx_msg->msg_done = ACPI_IPMI_OK;
+
 out_comp:
 	complete(&tx_msg->tx_complete);
 	acpi_ipmi_msg_put(tx_msg);
 out_msg:
 	ipmi_free_recv_msg(msg);
-};
+}
 
 static void ipmi_register_bmc(int iface, struct device *dev)
 {
@@ -436,7 +459,6 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 	acpi_handle handle;
 
 	err = ipmi_get_smi_info(iface, &smi_data);
-
 	if (err)
 		return;
 
@@ -461,11 +483,11 @@ static void ipmi_register_bmc(int iface, struct device *dev)
 		if (temp->handle == handle)
 			goto err_lock;
 	}
-
 	if (!driver_data.selected_smi)
 		driver_data.selected_smi = ipmi_device;
 	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
 	mutex_unlock(&driver_data.ipmi_lock);
+
 	put_device(smi_data.dev);
 	return;
 
@@ -484,7 +506,7 @@ static void ipmi_bmc_gone(int iface)
 
 	mutex_lock(&driver_data.ipmi_lock);
 	list_for_each_entry_safe(ipmi_device, temp,
-				&driver_data.ipmi_devices, head) {
+				 &driver_data.ipmi_devices, head) {
 		if (ipmi_device->ipmi_ifnum != iface) {
 			dev_found = true;
 			__ipmi_dev_kill(ipmi_device);
@@ -496,14 +518,13 @@ static void ipmi_bmc_gone(int iface)
 					&driver_data.ipmi_devices,
 					struct acpi_ipmi_device, head);
 	mutex_unlock(&driver_data.ipmi_lock);
+
 	if (dev_found) {
 		ipmi_flush_tx_msg(ipmi_device);
 		acpi_ipmi_dev_put(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
@@ -516,17 +537,17 @@ static void ipmi_bmc_gone(int iface)
  *	     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)
+			u32 bits, acpi_integer *value,
+			void *handler_context, void *region_context)
 {
 	struct acpi_ipmi_msg *tx_msg;
 	struct acpi_ipmi_device *ipmi_device;
 	int err;
 	acpi_status status;
 	unsigned long flags;
+
 	/*
 	 * IPMI opregion message.
 	 * IPMI message is firstly written to the BMC and system software
@@ -539,13 +560,13 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	tx_msg = ipmi_msg_alloc();
 	if (!tx_msg)
 		return AE_NOT_EXIST;
-
 	ipmi_device = tx_msg->device;
 
 	if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
 		ipmi_msg_release(tx_msg);
 		return AE_TYPE;
 	}
+
 	acpi_ipmi_msg_get(tx_msg);
 	mutex_lock(&driver_data.ipmi_lock);
 	/* Do not add a tx_msg that can not be flushed. */
@@ -558,16 +579,18 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
 	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
 	spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
 	mutex_unlock(&driver_data.ipmi_lock);
+
 	err = ipmi_request_settime(ipmi_device->user_interface,
-					&tx_msg->addr,
-					tx_msg->tx_msgid,
-					&tx_msg->tx_message,
-					NULL, 0, 0, IPMI_TIMEOUT);
+				   &tx_msg->addr,
+				   tx_msg->tx_msgid,
+				   &tx_msg->tx_message,
+				   NULL, 0, 0, IPMI_TIMEOUT);
 	if (err) {
 		status = AE_ERROR;
 		goto out_msg;
 	}
 	wait_for_completion(&tx_msg->tx_complete);
+
 	acpi_format_ipmi_response(tx_msg, value);
 	status = AE_OK;
 
@@ -586,8 +609,9 @@ static int __init acpi_ipmi_init(void)
 		return 0;
 
 	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler,
-				NULL, NULL);
+						    ACPI_ADR_SPACE_IPMI,
+						    &acpi_ipmi_space_handler,
+						    NULL, NULL);
 	if (ACPI_FAILURE(status)) {
 		pr_warn("Can't register IPMI opregion space handle\n");
 		return -EINVAL;
@@ -629,7 +653,8 @@ static void __exit acpi_ipmi_exit(void)
 	}
 	mutex_unlock(&driver_data.ipmi_lock);
 	acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler);
+					  ACPI_ADR_SPACE_IPMI,
+					  &acpi_ipmi_space_handler);
 }
 
 module_init(acpi_ipmi_init);
Signature · 1 line
1.7.10
#60 lore raw
In reply to Lv Zheng
Quoted 46 lines · Lv Zheng

This patchset tries to fix the following kernel bug:

This is fixed by [PATCH 06].

The bug shows IPMI operation region may appear in a device not under the IPMI system interface device's scope, thus it's required to install the ACPI IPMI operation region handler from the root of the ACPI namespace.

The original acpi_ipmi implementation includes several issues that break the test process. This patchset also includes a re-design of acpi_ipmi module to make the test possible.

[PATCH 01-06] are bug-fix patches that can be applied to the kernels whose
version is > 2.6.38. This can be confirmed with:
# git tag --contains e92b297c
[PATCH 07] is a tuning patch for acpi_ipmi.c.
[PATCH 08-12] are cleanup patches for acpi_ipmi.c and its Kconfig item.

v2.0
PATCH 03: Uses timeout mechanism offerred by ipmi_si.
PATCH 05: Uses kref instead of atomic_t and adds "dead" flag to kill the
atomic_read codes in ipmi_flush_tx_msg().
PATCH 07: Uses kref instead of atomic_t.

This patchset has passed the test around a fake device accessing IPMI operation region fields on an IPMI capable platform. A stress test of module(acpi_ipmi) load/unload has been performed on such platform. No races can be found and the IPMI operation region handler is functioning now. It is not possible to test module(ipmi_si) load/unload as it can't be unloaded due to its' transfer flushing implementation.

Lv Zheng (12):
ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler() ACPI/IPMI: Fix potential response buffer overflow ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers ACPI/IPMI: Fix race caused by the timed out ACPI IPMI transfers ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user ACPI/IPMI: Fix issue caused by the per-device registration of the
IPMI operation region handler
ACPI/IPMI: Add reference counting for ACPI IPMI transfers ACPI/IPMI: Cleanup several acpi_ipmi_device members ACPI/IPMI: Cleanup some initialization codes ACPI/IPMI: Cleanup some inclusion codes
ACPI/IPMI: Cleanup some Kconfig codes
ACPI/IPMI: Cleanup coding styles

Quoted 3 lines
 drivers/acpi/Kconfig     |    3 +-
 drivers/acpi/acpi_ipmi.c |  594 ++++++++++++++++++++++++++++------------------
 2 files changed, 367 insertions(+), 230 deletions(-)

Queued up for 3.13, thanks Lv!

author
#61 lore raw
Quoted 50 lines

From: Rafael J. Wysocki [mailto:[email protected]]
Sent: Thursday, September 26, 2013 1:55 AM

Quoted 46 lines · Lv Zheng

This patchset tries to fix the following kernel bug:

This is fixed by [PATCH 06].

The bug shows IPMI operation region may appear in a device not under the IPMI system interface device's scope, thus it's required to install the ACPI IPMI operation region handler from the root of the ACPI namespace.

The original acpi_ipmi implementation includes several issues that break the test process. This patchset also includes a re-design of acpi_ipmi module to make the test possible.

[PATCH 01-06] are bug-fix patches that can be applied to the kernels whose
version is > 2.6.38. This can be confirmed with:
# git tag --contains e92b297c
[PATCH 07] is a tuning patch for acpi_ipmi.c.
[PATCH 08-12] are cleanup patches for acpi_ipmi.c and its Kconfig item.

v2.0
PATCH 03: Uses timeout mechanism offerred by ipmi_si.
PATCH 05: Uses kref instead of atomic_t and adds "dead" flag to kill the
atomic_read codes in ipmi_flush_tx_msg().
PATCH 07: Uses kref instead of atomic_t.

This patchset has passed the test around a fake device accessing IPMI operation region fields on an IPMI capable platform. A stress test of module(acpi_ipmi) load/unload has been performed on such platform. No races can be found and the IPMI operation region handler is functioning now. It is not possible to test module(ipmi_si) load/unload as it can't be unloaded due to its' transfer flushing implementation.

Lv Zheng (12):
ACPI/IPMI: Fix atomic context requirement of ipmi_msg_handler() ACPI/IPMI: Fix potential response buffer overflow ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI transfers ACPI/IPMI: Fix race caused by the timed out ACPI IPMI transfers ACPI/IPMI: Fix race caused by the unprotected ACPI IPMI user ACPI/IPMI: Fix issue caused by the per-device registration of the
IPMI operation region handler
ACPI/IPMI: Add reference counting for ACPI IPMI transfers ACPI/IPMI: Cleanup several acpi_ipmi_device members ACPI/IPMI: Cleanup some initialization codes ACPI/IPMI: Cleanup some inclusion codes
ACPI/IPMI: Cleanup some Kconfig codes
ACPI/IPMI: Cleanup coding styles

Quoted 5 lines
Quoted 3 lines
 drivers/acpi/Kconfig     |    3 +-
 drivers/acpi/acpi_ipmi.c |  594 ++++++++++++++++++++++++++++------------------
 2 files changed, 367 insertions(+), 230 deletions(-)

Queued up for 3.13, thanks Lv!

Thanks for the helping.

Cheers
-Lv