|
@@ -106,9 +106,9 @@ static const struct {
|
|
* @channels: list of all channels detected on this edge
|
|
* @channels: list of all channels detected on this edge
|
|
* @channels_lock: guard for modifications of @channels
|
|
* @channels_lock: guard for modifications of @channels
|
|
* @allocated: array of bitmaps representing already allocated channels
|
|
* @allocated: array of bitmaps representing already allocated channels
|
|
- * @need_rescan: flag that the @work needs to scan smem for new channels
|
|
|
|
* @smem_available: last available amount of smem triggering a channel scan
|
|
* @smem_available: last available amount of smem triggering a channel scan
|
|
- * @work: work item for edge house keeping
|
|
|
|
|
|
+ * @scan_work: work item for discovering new channels
|
|
|
|
+ * @state_work: work item for edge state changes
|
|
*/
|
|
*/
|
|
struct qcom_smd_edge {
|
|
struct qcom_smd_edge {
|
|
struct qcom_smd *smd;
|
|
struct qcom_smd *smd;
|
|
@@ -127,10 +127,10 @@ struct qcom_smd_edge {
|
|
|
|
|
|
DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE);
|
|
DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE);
|
|
|
|
|
|
- bool need_rescan;
|
|
|
|
unsigned smem_available;
|
|
unsigned smem_available;
|
|
|
|
|
|
- struct work_struct work;
|
|
|
|
|
|
+ struct work_struct scan_work;
|
|
|
|
+ struct work_struct state_work;
|
|
};
|
|
};
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -614,7 +614,8 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
|
|
struct qcom_smd_edge *edge = data;
|
|
struct qcom_smd_edge *edge = data;
|
|
struct qcom_smd_channel *channel;
|
|
struct qcom_smd_channel *channel;
|
|
unsigned available;
|
|
unsigned available;
|
|
- bool kick_worker = false;
|
|
|
|
|
|
+ bool kick_scanner = false;
|
|
|
|
+ bool kick_state = false;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Handle state changes or data on each of the channels on this edge
|
|
* Handle state changes or data on each of the channels on this edge
|
|
@@ -622,7 +623,7 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
|
|
spin_lock(&edge->channels_lock);
|
|
spin_lock(&edge->channels_lock);
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
spin_lock(&channel->recv_lock);
|
|
spin_lock(&channel->recv_lock);
|
|
- kick_worker |= qcom_smd_channel_intr(channel);
|
|
|
|
|
|
+ kick_state |= qcom_smd_channel_intr(channel);
|
|
spin_unlock(&channel->recv_lock);
|
|
spin_unlock(&channel->recv_lock);
|
|
}
|
|
}
|
|
spin_unlock(&edge->channels_lock);
|
|
spin_unlock(&edge->channels_lock);
|
|
@@ -635,12 +636,13 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
|
|
available = qcom_smem_get_free_space(edge->remote_pid);
|
|
available = qcom_smem_get_free_space(edge->remote_pid);
|
|
if (available != edge->smem_available) {
|
|
if (available != edge->smem_available) {
|
|
edge->smem_available = available;
|
|
edge->smem_available = available;
|
|
- edge->need_rescan = true;
|
|
|
|
- kick_worker = true;
|
|
|
|
|
|
+ kick_scanner = true;
|
|
}
|
|
}
|
|
|
|
|
|
- if (kick_worker)
|
|
|
|
- schedule_work(&edge->work);
|
|
|
|
|
|
+ if (kick_scanner)
|
|
|
|
+ schedule_work(&edge->scan_work);
|
|
|
|
+ if (kick_state)
|
|
|
|
+ schedule_work(&edge->state_work);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
@@ -1098,8 +1100,9 @@ free_name_and_channel:
|
|
* qcom_smd_create_channel() to create representations of these and add
|
|
* qcom_smd_create_channel() to create representations of these and add
|
|
* them to the edge's list of channels.
|
|
* them to the edge's list of channels.
|
|
*/
|
|
*/
|
|
-static void qcom_discover_channels(struct qcom_smd_edge *edge)
|
|
|
|
|
|
+static void qcom_channel_scan_worker(struct work_struct *work)
|
|
{
|
|
{
|
|
|
|
+ struct qcom_smd_edge *edge = container_of(work, struct qcom_smd_edge, scan_work);
|
|
struct qcom_smd_alloc_entry *alloc_tbl;
|
|
struct qcom_smd_alloc_entry *alloc_tbl;
|
|
struct qcom_smd_alloc_entry *entry;
|
|
struct qcom_smd_alloc_entry *entry;
|
|
struct qcom_smd_channel *channel;
|
|
struct qcom_smd_channel *channel;
|
|
@@ -1152,7 +1155,7 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- schedule_work(&edge->work);
|
|
|
|
|
|
+ schedule_work(&edge->state_work);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1160,29 +1163,23 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)
|
|
* then scans all registered channels for state changes that should be handled
|
|
* then scans all registered channels for state changes that should be handled
|
|
* by creating or destroying smd client devices for the registered channels.
|
|
* by creating or destroying smd client devices for the registered channels.
|
|
*
|
|
*
|
|
- * LOCKING: edge->channels_lock is not needed to be held during the traversal
|
|
|
|
- * of the channels list as it's done synchronously with the only writer.
|
|
|
|
|
|
+ * LOCKING: edge->channels_lock only needs to cover the list operations, as the
|
|
|
|
+ * worker is killed before any channels are deallocated
|
|
*/
|
|
*/
|
|
static void qcom_channel_state_worker(struct work_struct *work)
|
|
static void qcom_channel_state_worker(struct work_struct *work)
|
|
{
|
|
{
|
|
struct qcom_smd_channel *channel;
|
|
struct qcom_smd_channel *channel;
|
|
struct qcom_smd_edge *edge = container_of(work,
|
|
struct qcom_smd_edge *edge = container_of(work,
|
|
struct qcom_smd_edge,
|
|
struct qcom_smd_edge,
|
|
- work);
|
|
|
|
|
|
+ state_work);
|
|
unsigned remote_state;
|
|
unsigned remote_state;
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Rescan smem if we have reason to belive that there are new channels.
|
|
|
|
- */
|
|
|
|
- if (edge->need_rescan) {
|
|
|
|
- edge->need_rescan = false;
|
|
|
|
- qcom_discover_channels(edge);
|
|
|
|
- }
|
|
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Register a device for any closed channel where the remote processor
|
|
* Register a device for any closed channel where the remote processor
|
|
* is showing interest in opening the channel.
|
|
* is showing interest in opening the channel.
|
|
*/
|
|
*/
|
|
|
|
+ spin_lock_irqsave(&edge->channels_lock, flags);
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
if (channel->state != SMD_CHANNEL_CLOSED)
|
|
if (channel->state != SMD_CHANNEL_CLOSED)
|
|
continue;
|
|
continue;
|
|
@@ -1192,7 +1189,9 @@ static void qcom_channel_state_worker(struct work_struct *work)
|
|
remote_state != SMD_CHANNEL_OPENED)
|
|
remote_state != SMD_CHANNEL_OPENED)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
|
|
qcom_smd_create_device(channel);
|
|
qcom_smd_create_device(channel);
|
|
|
|
+ spin_lock_irqsave(&edge->channels_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1209,8 +1208,11 @@ static void qcom_channel_state_worker(struct work_struct *work)
|
|
remote_state == SMD_CHANNEL_OPENED)
|
|
remote_state == SMD_CHANNEL_OPENED)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
|
|
qcom_smd_destroy_device(channel);
|
|
qcom_smd_destroy_device(channel);
|
|
|
|
+ spin_lock_irqsave(&edge->channels_lock, flags);
|
|
}
|
|
}
|
|
|
|
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1228,7 +1230,8 @@ static int qcom_smd_parse_edge(struct device *dev,
|
|
INIT_LIST_HEAD(&edge->channels);
|
|
INIT_LIST_HEAD(&edge->channels);
|
|
spin_lock_init(&edge->channels_lock);
|
|
spin_lock_init(&edge->channels_lock);
|
|
|
|
|
|
- INIT_WORK(&edge->work, qcom_channel_state_worker);
|
|
|
|
|
|
+ INIT_WORK(&edge->scan_work, qcom_channel_scan_worker);
|
|
|
|
+ INIT_WORK(&edge->state_work, qcom_channel_state_worker);
|
|
|
|
|
|
edge->of_node = of_node_get(node);
|
|
edge->of_node = of_node_get(node);
|
|
|
|
|
|
@@ -1317,8 +1320,7 @@ static int qcom_smd_probe(struct platform_device *pdev)
|
|
if (ret)
|
|
if (ret)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- edge->need_rescan = true;
|
|
|
|
- schedule_work(&edge->work);
|
|
|
|
|
|
+ schedule_work(&edge->scan_work);
|
|
}
|
|
}
|
|
|
|
|
|
platform_set_drvdata(pdev, smd);
|
|
platform_set_drvdata(pdev, smd);
|
|
@@ -1341,8 +1343,10 @@ static int qcom_smd_remove(struct platform_device *pdev)
|
|
edge = &smd->edges[i];
|
|
edge = &smd->edges[i];
|
|
|
|
|
|
disable_irq(edge->irq);
|
|
disable_irq(edge->irq);
|
|
- cancel_work_sync(&edge->work);
|
|
|
|
|
|
+ cancel_work_sync(&edge->scan_work);
|
|
|
|
+ cancel_work_sync(&edge->state_work);
|
|
|
|
|
|
|
|
+ /* No need to lock here, because the writer is gone */
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
list_for_each_entry(channel, &edge->channels, list) {
|
|
if (!channel->qsdev)
|
|
if (!channel->qsdev)
|
|
continue;
|
|
continue;
|