Linux Audio

Check our new training course

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