Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93f68f1e authored by Nathan Fontenot's avatar Nathan Fontenot Committed by Benjamin Herrenschmidt
Browse files

powerpc/pseries: Correct rtas_data_buf locking in dlpar code



The dlpar code can cause a deadlock to occur when making the RTAS
configure-connector call.  This occurs because we make kmalloc calls,
which can block, while parsing the rtas_data_buf and holding the
rtas_data_buf_lock.  This an cause issues if someone else attempts
to grab the rtas_data_bug_lock.

This patch alleviates this issue by copying the contents of the rtas_data_buf
to a local buffer before parsing.  This allows us to only hold the
rtas_data_buf_lock around the RTAS configure-connector calls.

Signed-off-by: default avatarNathan Fontenot <nfont@austin.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent a28dec2f
Loading
Loading
Loading
Loading
+29 −13
Original line number Diff line number Diff line
@@ -129,20 +129,35 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
	struct property *property;
	struct property *last_property = NULL;
	struct cc_workarea *ccwa;
	char *data_buf;
	int cc_token;
	int rc;
	int rc = -1;

	cc_token = rtas_token("ibm,configure-connector");
	if (cc_token == RTAS_UNKNOWN_SERVICE)
		return NULL;

	spin_lock(&rtas_data_buf_lock);
	ccwa = (struct cc_workarea *)&rtas_data_buf[0];
	data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
	if (!data_buf)
		return NULL;

	ccwa = (struct cc_workarea *)&data_buf[0];
	ccwa->drc_index = drc_index;
	ccwa->zero = 0;

	do {
		/* Since we release the rtas_data_buf lock between configure
		 * connector calls we want to re-populate the rtas_data_buffer
		 * with the contents of the previous call.
		 */
		spin_lock(&rtas_data_buf_lock);

		memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
	while (rc) {
		memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);

		spin_unlock(&rtas_data_buf_lock);

		switch (rc) {
		case NEXT_SIBLING:
			dn = dlpar_parse_cc_node(ccwa);
@@ -197,20 +212,21 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
			       "returned from configure-connector\n", rc);
			goto cc_error;
		}

		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
	}

	spin_unlock(&rtas_data_buf_lock);
	return first_dn;
	} while (rc);

cc_error:
	kfree(data_buf);

	if (rc) {
		if (first_dn)
			dlpar_free_cc_nodes(first_dn);
	spin_unlock(&rtas_data_buf_lock);

		return NULL;
	}

	return first_dn;
}

static struct device_node *derive_parent(const char *path)
{
	struct device_node *parent;