Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Intel MIC Platform Software Stack (MPSS)
  4 *
  5 * Copyright(c) 2014 Intel Corporation.
  6 *
  7 * Intel SCIF driver.
  8 */
  9#include "scif_main.h"
 10#include "../bus/scif_bus.h"
 11#include "scif_peer_bus.h"
 12
 13static inline struct scif_peer_dev *
 14dev_to_scif_peer(struct device *dev)
 15{
 16	return container_of(dev, struct scif_peer_dev, dev);
 17}
 18
 19struct bus_type scif_peer_bus = {
 20	.name  = "scif_peer_bus",
 21};
 22
 23static void scif_peer_release_dev(struct device *d)
 24{
 25	struct scif_peer_dev *sdev = dev_to_scif_peer(d);
 26	struct scif_dev *scifdev = &scif_dev[sdev->dnode];
 27
 28	scif_cleanup_scifdev(scifdev);
 29	kfree(sdev);
 30}
 31
 32static int scif_peer_initialize_device(struct scif_dev *scifdev)
 33{
 34	struct scif_peer_dev *spdev;
 35	int ret;
 36
 37	spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
 38	if (!spdev) {
 39		ret = -ENOMEM;
 40		goto err;
 41	}
 42
 43	spdev->dev.parent = scifdev->sdev->dev.parent;
 44	spdev->dev.release = scif_peer_release_dev;
 45	spdev->dnode = scifdev->node;
 46	spdev->dev.bus = &scif_peer_bus;
 47	dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode);
 48
 49	device_initialize(&spdev->dev);
 50	get_device(&spdev->dev);
 51	rcu_assign_pointer(scifdev->spdev, spdev);
 52
 53	mutex_lock(&scif_info.conflock);
 54	scif_info.total++;
 55	scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
 56	mutex_unlock(&scif_info.conflock);
 57	return 0;
 58err:
 59	dev_err(&scifdev->sdev->dev,
 60		"dnode %d: initialize_device rc %d\n", scifdev->node, ret);
 61	return ret;
 62}
 63
 64static int scif_peer_add_device(struct scif_dev *scifdev)
 65{
 66	struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
 67	char pool_name[16];
 68	int ret;
 69
 70	ret = device_add(&spdev->dev);
 71	put_device(&spdev->dev);
 72	if (ret) {
 73		dev_err(&scifdev->sdev->dev,
 74			"dnode %d: peer device_add failed\n", scifdev->node);
 75		goto put_spdev;
 76	}
 77
 78	scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode);
 79	scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev,
 80						sizeof(struct scif_status), 1,
 81						0);
 82	if (!scifdev->signal_pool) {
 83		dev_err(&scifdev->sdev->dev,
 84			"dnode %d: dmam_pool_create failed\n", scifdev->node);
 85		ret = -ENOMEM;
 86		goto del_spdev;
 87	}
 88	dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
 89	return 0;
 90del_spdev:
 91	device_del(&spdev->dev);
 92put_spdev:
 93	RCU_INIT_POINTER(scifdev->spdev, NULL);
 94	synchronize_rcu();
 95	put_device(&spdev->dev);
 96
 97	mutex_lock(&scif_info.conflock);
 98	scif_info.total--;
 99	mutex_unlock(&scif_info.conflock);
100	return ret;
101}
102
103void scif_add_peer_device(struct work_struct *work)
104{
105	struct scif_dev *scifdev = container_of(work, struct scif_dev,
106						peer_add_work);
107
108	scif_peer_add_device(scifdev);
109}
110
111/*
112 * Peer device registration is split into a device_initialize and a device_add.
113 * The reason for doing this is as follows: First, peer device registration
114 * itself cannot be done in the message processing thread and must be delegated
115 * to another workqueue, otherwise if SCIF client probe, called during peer
116 * device registration, calls scif_connect(..), it will block the message
117 * processing thread causing a deadlock. Next, device_initialize is done in the
118 * "top-half" message processing thread and device_add in the "bottom-half"
119 * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing
120 * concurrently with SCIF_INIT message processing is unable to get a reference
121 * on the peer device, thereby failing the connect request.
122 */
123void scif_peer_register_device(struct scif_dev *scifdev)
124{
125	int ret;
126
127	mutex_lock(&scifdev->lock);
128	ret = scif_peer_initialize_device(scifdev);
129	if (ret)
130		goto exit;
131	schedule_work(&scifdev->peer_add_work);
132exit:
133	mutex_unlock(&scifdev->lock);
134}
135
136int scif_peer_unregister_device(struct scif_dev *scifdev)
137{
138	struct scif_peer_dev *spdev;
139
140	mutex_lock(&scifdev->lock);
141	/* Flush work to ensure device register is complete */
142	flush_work(&scifdev->peer_add_work);
143
144	/*
145	 * Continue holding scifdev->lock since theoretically unregister_device
146	 * can be called simultaneously from multiple threads
147	 */
148	spdev = rcu_dereference(scifdev->spdev);
149	if (!spdev) {
150		mutex_unlock(&scifdev->lock);
151		return -ENODEV;
152	}
153
154	RCU_INIT_POINTER(scifdev->spdev, NULL);
155	synchronize_rcu();
156	mutex_unlock(&scifdev->lock);
157
158	dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode);
159	device_unregister(&spdev->dev);
160
161	mutex_lock(&scif_info.conflock);
162	scif_info.total--;
163	mutex_unlock(&scif_info.conflock);
164	return 0;
165}
166
167int scif_peer_bus_init(void)
168{
169	return bus_register(&scif_peer_bus);
170}
171
172void scif_peer_bus_exit(void)
173{
174	bus_unregister(&scif_peer_bus);
175}