#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/xarray.h>
#include <linux/mailbox_client.h>
#include "rpmsg_internal.h"
#define GLINK_NAME_SIZE 32
#define GLINK_VERSION_1 1
-#define RPM_GLINK_CID_MIN 1
-#define RPM_GLINK_CID_MAX 65536
-
struct glink_msg {
__le16 cmd;
__le16 param1;
* @rx_lock: protects the @rx_queue
* @rx_queue: queue of received control messages to be processed in @rx_work
* @tx_lock: synchronizes operations on the tx fifo
- * @idr_lock: synchronizes @lcids and @rcids modifications
- * @lcids: idr of all channels with a known local channel id
+ * @idr_lock: synchronizes @rcids modifications
+ * @lcids: all channels with a known local channel id
* @rcids: idr of all channels with a known remote channel id
* @features: remote features
* @intentless: flag to indicate that there is no intent
spinlock_t tx_lock;
spinlock_t idr_lock;
- struct idr lcids;
+ u32 lcid_next;
+ struct xarray lcids;
struct idr rcids;
unsigned long features;
int name_len = strlen(channel->name) + 1;
int req_len = ALIGN(sizeof(req.msg) + name_len, 8);
int ret;
- unsigned long flags;
kref_get(&channel->refcount);
- spin_lock_irqsave(&glink->idr_lock, flags);
- ret = idr_alloc_cyclic(&glink->lcids, channel,
- RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX,
- GFP_ATOMIC);
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ ret = xa_alloc_cyclic_irq(&glink->lcids, &channel->lcid, channel,
+ XA_LIMIT(1, 65535), &glink->lcid_next, GFP_KERNEL);
if (ret < 0)
return ret;
- channel->lcid = ret;
-
req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN);
req.msg.param1 = cpu_to_le16(channel->lcid);
req.msg.param2 = cpu_to_le32(name_len);
strcpy(req.name, channel->name);
ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
- if (ret)
- goto remove_idr;
-
- return 0;
+ if (!ret)
+ return 0;
-remove_idr:
- spin_lock_irqsave(&glink->idr_lock, flags);
- idr_remove(&glink->lcids, channel->lcid);
+ xa_erase_irq(&glink->lcids, channel->lcid);
channel->lcid = 0;
- spin_unlock_irqrestore(&glink->idr_lock, flags);
-
return ret;
}
{
struct glink_channel *channel;
- spin_lock(&glink->idr_lock);
- channel = idr_find(&glink->lcids, lcid);
- spin_unlock(&glink->idr_lock);
+ channel = xa_load(&glink->lcids, lcid);
if (!channel) {
dev_err(glink->dev, "Invalid open ack packet\n");
return -EINVAL;
{
struct glink_channel *channel;
int ret;
- unsigned long flags;
channel = qcom_glink_alloc_channel(glink, name);
if (IS_ERR(channel))
err_timeout:
/* qcom_glink_send_open_req() did register the channel in lcids*/
- spin_lock_irqsave(&glink->idr_lock, flags);
- idr_remove(&glink->lcids, channel->lcid);
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ xa_erase_irq(&glink->lcids, channel->lcid);
release_channel:
/* Release qcom_glink_send_open_req() reference */
struct rpmsg_device *rpdev;
bool create_device = false;
struct device_node *node;
- int lcid;
+ unsigned long lcid;
int ret;
- unsigned long flags;
- spin_lock_irqsave(&glink->idr_lock, flags);
- idr_for_each_entry(&glink->lcids, channel, lcid) {
+ xa_lock_irq(&glink->lcids);
+ xa_for_each(&glink->lcids, lcid, channel) {
if (!strcmp(channel->name, name))
break;
}
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ xa_unlock_irq(&glink->lcids);
if (!channel) {
channel = qcom_glink_alloc_channel(glink, name);
create_device = true;
}
- spin_lock_irqsave(&glink->idr_lock, flags);
+ spin_lock_irq(&glink->idr_lock);
ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC);
if (ret < 0) {
dev_err(glink->dev, "Unable to insert channel into rcid list\n");
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ spin_unlock_irq(&glink->idr_lock);
goto free_channel;
}
channel->rcid = ret;
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ spin_unlock_irq(&glink->idr_lock);
complete(&channel->open_req);
free_rpdev:
kfree(rpdev);
rcid_remove:
- spin_lock_irqsave(&glink->idr_lock, flags);
+ spin_lock_irq(&glink->idr_lock);
idr_remove(&glink->rcids, channel->rcid);
channel->rcid = 0;
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ spin_unlock_irq(&glink->idr_lock);
free_channel:
/* Release the reference, iff we took it */
if (create_device)
struct glink_channel *channel;
unsigned long flags;
- spin_lock_irqsave(&glink->idr_lock, flags);
- channel = idr_find(&glink->lcids, lcid);
- if (WARN(!channel, "close ack on unknown channel\n")) {
- spin_unlock_irqrestore(&glink->idr_lock, flags);
+ xa_lock_irqsave(&glink->lcids, flags);
+ channel = __xa_erase(&glink->lcids, lcid);
+ xa_unlock_irqrestore(&glink->lcids, flags);
+ if (WARN(!channel, "close ack on unknown channel\n"))
return;
- }
- idr_remove(&glink->lcids, channel->lcid);
channel->lcid = 0;
- spin_unlock_irqrestore(&glink->idr_lock, flags);
-
kref_put(&channel->refcount, qcom_glink_channel_release);
}
INIT_WORK(&glink->rx_work, qcom_glink_work);
spin_lock_init(&glink->idr_lock);
- idr_init(&glink->lcids);
+ xa_init_flags(&glink->lcids, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
+ glink->lcid_next = 0;
idr_init(&glink->rcids);
ret = of_property_read_string(dev->of_node, "label", &glink->name);
void qcom_glink_native_remove(struct qcom_glink *glink)
{
struct glink_channel *channel;
- int cid;
+ unsigned long cid;
int ret;
unsigned long flags;
if (ret)
dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
- spin_lock_irqsave(&glink->idr_lock, flags);
+ xa_lock_irqsave(&glink->lcids, flags);
/* Release any defunct local channels, waiting for close-ack */
- idr_for_each_entry(&glink->lcids, channel, cid)
+ xa_for_each(&glink->lcids, cid, channel)
kref_put(&channel->refcount, qcom_glink_channel_release);
+ xa_unlock_irqrestore(&glink->lcids, flags);
- idr_destroy(&glink->lcids);
+ xa_destroy(&glink->lcids);
idr_destroy(&glink->rcids);
- spin_unlock_irqrestore(&glink->idr_lock, flags);
mbox_free_channel(glink->mbox_chan);
}
EXPORT_SYMBOL_GPL(qcom_glink_native_remove);