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

Commit d3d912eb authored by Ashutosh Dixit's avatar Ashutosh Dixit Committed by Greg Kroah-Hartman
Browse files

misc: mic: Add support for kernel mode SCIF clients



Add support for registration/de-registration of kernel mode SCIF
clients. SCIF clients are probed with new and existing SCIF peer
devices. Similarly the client remove method is called when SCIF
peer devices are removed.

Changes to SCIF peer device framework necessitated by supporting
kernel mode SCIF clients are also included in this patch.

Reviewed-by: default avatarNikhil Rao <nikhil.rao@intel.com>
Reviewed-by: default avatarSudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: default avatarAshutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b7f94441
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -1430,3 +1430,46 @@ int scif_get_node_ids(u16 *nodes, int len, u16 *self)
	return online;
}
EXPORT_SYMBOL_GPL(scif_get_node_ids);

static int scif_add_client_dev(struct device *dev, struct subsys_interface *si)
{
	struct scif_client *client =
		container_of(si, struct scif_client, si);
	struct scif_peer_dev *spdev =
		container_of(dev, struct scif_peer_dev, dev);

	if (client->probe)
		client->probe(spdev);
	return 0;
}

static void scif_remove_client_dev(struct device *dev,
				   struct subsys_interface *si)
{
	struct scif_client *client =
		container_of(si, struct scif_client, si);
	struct scif_peer_dev *spdev =
		container_of(dev, struct scif_peer_dev, dev);

	if (client->remove)
		client->remove(spdev);
}

void scif_client_unregister(struct scif_client *client)
{
	subsys_interface_unregister(&client->si);
}
EXPORT_SYMBOL_GPL(scif_client_unregister);

int scif_client_register(struct scif_client *client)
{
	struct subsys_interface *si = &client->si;

	si->name = client->name;
	si->subsys = &scif_peer_bus;
	si->add_dev = scif_add_client_dev;
	si->remove_dev = scif_remove_client_dev;

	return subsys_interface_register(&client->si);
}
EXPORT_SYMBOL_GPL(scif_client_register);
+19 −69
Original line number Diff line number Diff line
@@ -80,35 +80,6 @@ irqreturn_t scif_intr_handler(int irq, void *data)
	return IRQ_HANDLED;
}

static int scif_peer_probe(struct scif_peer_dev *spdev)
{
	struct scif_dev *scifdev = &scif_dev[spdev->dnode];

	mutex_lock(&scif_info.conflock);
	scif_info.total++;
	scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
	mutex_unlock(&scif_info.conflock);
	rcu_assign_pointer(scifdev->spdev, spdev);

	/* In the future SCIF kernel client devices will be added here */
	return 0;
}

static void scif_peer_remove(struct scif_peer_dev *spdev)
{
	struct scif_dev *scifdev = &scif_dev[spdev->dnode];

	/* In the future SCIF kernel client devices will be removed here */
	spdev = rcu_dereference(scifdev->spdev);
	if (spdev)
		RCU_INIT_POINTER(scifdev->spdev, NULL);
	synchronize_rcu();

	mutex_lock(&scif_info.conflock);
	scif_info.total--;
	mutex_unlock(&scif_info.conflock);
}

static void scif_qp_setup_handler(struct work_struct *work)
{
	struct scif_dev *scifdev = container_of(work, struct scif_dev,
@@ -139,20 +110,13 @@ static void scif_qp_setup_handler(struct work_struct *work)
	}
}

static int scif_setup_scifdev(struct scif_hw_dev *sdev)
static int scif_setup_scifdev(void)
{
	/* We support a maximum of 129 SCIF nodes including the mgmt node */
#define MAX_SCIF_NODES 129
	int i;
	u8 num_nodes;

	if (sdev->snode) {
		struct mic_bootparam __iomem *bp = sdev->rdp;

		num_nodes = ioread8(&bp->tot_nodes);
	} else {
		struct mic_bootparam *bp = sdev->dp;
	u8 num_nodes = MAX_SCIF_NODES;

		num_nodes = bp->tot_nodes;
	}
	scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL);
	if (!scif_dev)
		return -ENOMEM;
@@ -163,7 +127,7 @@ static int scif_setup_scifdev(struct scif_hw_dev *sdev)
		scifdev->exit = OP_IDLE;
		init_waitqueue_head(&scifdev->disconn_wq);
		mutex_init(&scifdev->lock);
		INIT_WORK(&scifdev->init_msg_work, scif_qp_response_ack);
		INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device);
		INIT_DELAYED_WORK(&scifdev->p2p_dwork,
				  scif_poll_qp_state);
		INIT_DELAYED_WORK(&scifdev->qp_dwork,
@@ -181,27 +145,21 @@ static void scif_destroy_scifdev(void)

static int scif_probe(struct scif_hw_dev *sdev)
{
	struct scif_dev *scifdev;
	struct scif_dev *scifdev = &scif_dev[sdev->dnode];
	int rc;

	dev_set_drvdata(&sdev->dev, sdev);
	scifdev->sdev = sdev;

	if (1 == atomic_add_return(1, &g_loopb_cnt)) {
		struct scif_dev *loopb_dev;
		struct scif_dev *loopb_dev = &scif_dev[sdev->snode];

		rc = scif_setup_scifdev(sdev);
		if (rc)
			goto exit;
		scifdev = &scif_dev[sdev->dnode];
		scifdev->sdev = sdev;
		loopb_dev = &scif_dev[sdev->snode];
		loopb_dev->sdev = sdev;
		rc = scif_setup_loopback_qp(loopb_dev);
		if (rc)
			goto free_sdev;
	} else {
		scifdev = &scif_dev[sdev->dnode];
		scifdev->sdev = sdev;
			goto exit;
	}

	rc = scif_setup_intr_wq(scifdev);
	if (rc)
		goto destroy_loopb;
@@ -237,8 +195,6 @@ static int scif_probe(struct scif_hw_dev *sdev)
destroy_loopb:
	if (atomic_dec_and_test(&g_loopb_cnt))
		scif_destroy_loopback_qp(&scif_dev[sdev->snode]);
free_sdev:
	scif_destroy_scifdev();
exit:
	return rc;
}
@@ -290,13 +246,6 @@ static void scif_remove(struct scif_hw_dev *sdev)
	scifdev->sdev = NULL;
}

static struct scif_peer_driver scif_peer_driver = {
	.driver.name =	KBUILD_MODNAME,
	.driver.owner =	THIS_MODULE,
	.probe = scif_peer_probe,
	.remove = scif_peer_remove,
};

static struct scif_hw_dev_id id_table[] = {
	{ MIC_SCIF_DEV, SCIF_DEV_ANY_ID },
	{ 0 },
@@ -312,6 +261,8 @@ static struct scif_driver scif_driver = {

static int _scif_init(void)
{
	int rc;

	spin_lock_init(&scif_info.eplock);
	spin_lock_init(&scif_info.nb_connect_lock);
	spin_lock_init(&scif_info.port_lock);
@@ -326,10 +277,15 @@ static int _scif_init(void)
	init_waitqueue_head(&scif_info.exitwq);
	scif_info.en_msg_log = 0;
	scif_info.p2p_enable = 1;
	rc = scif_setup_scifdev();
	if (rc)
		goto error;
	INIT_WORK(&scif_info.misc_work, scif_misc_handler);
	INIT_WORK(&scif_info.conn_work, scif_conn_handler);
	idr_init(&scif_ports);
	return 0;
error:
	return rc;
}

static void _scif_exit(void)
@@ -347,12 +303,9 @@ static int __init scif_init(void)
	rc = scif_peer_bus_init();
	if (rc)
		goto exit;
	rc = scif_peer_register_driver(&scif_peer_driver);
	if (rc)
		goto peer_bus_exit;
	rc = scif_register_driver(&scif_driver);
	if (rc)
		goto unreg_scif_peer;
		goto peer_bus_exit;
	rc = misc_register(mdev);
	if (rc)
		goto unreg_scif;
@@ -360,8 +313,6 @@ static int __init scif_init(void)
	return 0;
unreg_scif:
	scif_unregister_driver(&scif_driver);
unreg_scif_peer:
	scif_peer_unregister_driver(&scif_peer_driver);
peer_bus_exit:
	scif_peer_bus_exit();
exit:
@@ -374,7 +325,6 @@ static void __exit scif_exit(void)
	scif_exit_debugfs();
	misc_deregister(&scif_info.mdev);
	scif_unregister_driver(&scif_driver);
	scif_peer_unregister_driver(&scif_peer_driver);
	scif_peer_bus_exit();
	_scif_exit();
}
+3 −2
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ struct scif_p2p_info {
 * @db: doorbell the peer will trigger to generate an interrupt on self
 * @rdb: Doorbell to trigger on the peer to generate an interrupt on the peer
 * @cookie: Cookie received while registering the interrupt handler
 * init_msg_work: work scheduled for SCIF_INIT message processing
 * @peer_add_work: Work for handling device_add for peer devices
 * @p2p_dwork: Delayed work to enable polling for P2P state
 * @qp_dwork: Delayed work for enabling polling for remote QP information
 * @p2p_retry: Number of times to retry polling of P2P state
@@ -166,7 +166,7 @@ struct scif_dev {
	int db;
	int rdb;
	struct mic_irq *cookie;
	struct work_struct init_msg_work;
	struct work_struct peer_add_work;
	struct delayed_work p2p_dwork;
	struct delayed_work qp_dwork;
	int p2p_retry;
@@ -183,6 +183,7 @@ struct scif_dev {

extern struct scif_info scif_info;
extern struct idr scif_ports;
extern struct bus_type scif_peer_bus;
extern struct scif_dev *scif_dev;
extern const struct file_operations scif_fops;
extern const struct file_operations scif_anon_fops;
+2 −8
Original line number Diff line number Diff line
@@ -147,14 +147,8 @@ void scif_cleanup_scifdev(struct scif_dev *dev)
void scif_handle_remove_node(int node)
{
	struct scif_dev *scifdev = &scif_dev[node];
	struct scif_peer_dev *spdev;

	rcu_read_lock();
	spdev = rcu_dereference(scifdev->spdev);
	rcu_read_unlock();
	if (spdev)
		scif_peer_unregister_device(spdev);
	else

	if (scif_peer_unregister_device(scifdev))
		scif_send_acks(scifdev);
}

+21 −44
Original line number Diff line number Diff line
@@ -259,6 +259,11 @@ int scif_setup_qp_connect_response(struct scif_dev *scifdev,
		     &qp->remote_qp->local_write,
		     r_buf,
		     get_count_order(remote_size));
	/*
	 * Because the node QP may already be processing an INIT message, set
	 * the read pointer so the cached read offset isn't lost
	 */
	qp->remote_qp->local_read = qp->inbound_q.current_read_offset;
	/*
	 * resetup the inbound_q now that we know where the
	 * inbound_read really is.
@@ -529,27 +534,6 @@ static void scif_p2p_setup(void)
	}
}

void scif_qp_response_ack(struct work_struct *work)
{
	struct scif_dev *scifdev = container_of(work, struct scif_dev,
						init_msg_work);
	struct scif_peer_dev *spdev;

	/* Drop the INIT message if it has already been received */
	if (_scifdev_alive(scifdev))
		return;

	spdev = scif_peer_register_device(scifdev);
	if (IS_ERR(spdev))
		return;

	if (scif_is_mgmt_node()) {
		mutex_lock(&scif_info.conflock);
		scif_p2p_setup();
		mutex_unlock(&scif_info.conflock);
	}
}

static char *message_types[] = {"BAD",
				"INIT",
				"EXIT",
@@ -682,13 +666,14 @@ scif_init(struct scif_dev *scifdev, struct scifmsg *msg)
	 * address to complete initializing the inbound_q.
	 */
	flush_delayed_work(&scifdev->qp_dwork);
	/*
	 * Delegate the peer device registration to a workqueue, otherwise if
	 * SCIF client probe (called during peer device registration) calls
	 * scif_connect(..), it will block the message processing thread causing
	 * a deadlock.
	 */
	schedule_work(&scifdev->init_msg_work);

	scif_peer_register_device(scifdev);

	if (scif_is_mgmt_node()) {
		mutex_lock(&scif_info.conflock);
		scif_p2p_setup();
		mutex_unlock(&scif_info.conflock);
	}
}

/**
@@ -838,13 +823,13 @@ void scif_poll_qp_state(struct work_struct *work)
				      msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT));
		return;
	}
	scif_peer_register_device(peerdev);
	return;
timeout:
	dev_err(&peerdev->sdev->dev,
		"%s %d remote node %d offline,  state = 0x%x\n",
		__func__, __LINE__, peerdev->node, qp->qp_state);
	qp->remote_qp->qp_state = SCIF_QP_OFFLINE;
	scif_peer_unregister_device(peerdev);
	scif_cleanup_scifdev(peerdev);
}

@@ -894,6 +879,9 @@ scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg)
		goto local_error;
	peerdev->rdb = msg->payload[2];
	qp->remote_qp->qp_state = SCIF_QP_ONLINE;

	scif_peer_register_device(peerdev);

	schedule_delayed_work(&peerdev->p2p_dwork, 0);
	return;
local_error:
@@ -1169,7 +1157,6 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev)
	int err = 0;
	void *local_q;
	struct scif_qp *qp;
	struct scif_peer_dev *spdev;

	err = scif_setup_intr_wq(scifdev);
	if (err)
@@ -1216,15 +1203,11 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev)
		     &qp->local_write,
		     local_q, get_count_order(SCIF_NODE_QP_SIZE));
	scif_info.nodeid = scifdev->node;
	spdev = scif_peer_register_device(scifdev);
	if (IS_ERR(spdev)) {
		err = PTR_ERR(spdev);
		goto free_local_q;
	}

	scif_peer_register_device(scifdev);

	scif_info.loopb_dev = scifdev;
	return err;
free_local_q:
	kfree(local_q);
free_qpairs:
	kfree(scifdev->qpairs);
destroy_loopb_wq:
@@ -1243,13 +1226,7 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev)
 */
int scif_destroy_loopback_qp(struct scif_dev *scifdev)
{
	struct scif_peer_dev *spdev;

	rcu_read_lock();
	spdev = rcu_dereference(scifdev->spdev);
	rcu_read_unlock();
	if (spdev)
		scif_peer_unregister_device(spdev);
	scif_peer_unregister_device(scifdev);
	destroy_workqueue(scif_info.loopb_wq);
	scif_destroy_intr_wq(scifdev);
	kfree(scifdev->qpairs->outbound_q.rb_base);
Loading