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

Commit fec1a593 authored by Stefano Panella's avatar Stefano Panella Committed by David Vrabel
Browse files

uwb: per-radio controller event thread and beacon cache



Use an event thread per-radio controller so processing events from one
radio controller doesn't delay another.

A radio controller shouldn't have information on devices seen by a
different radio controller (they may be on different channels) so make the
beacon cache per-radio controller.

Signed-off-by: default avatarStefano Panella <stefano.panella@csr.com>
Signed-off-by: default avatarDavid Vrabel <david.vrabel@csr.com>
parent 6d5a681d
Loading
Loading
Loading
Loading
+30 −31
Original line number Diff line number Diff line
@@ -168,12 +168,6 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
 * FIXME: use something faster for search than a list
 */

struct uwb_beca uwb_beca = {
	.list = LIST_HEAD_INIT(uwb_beca.list),
	.mutex = __MUTEX_INITIALIZER(uwb_beca.mutex)
};


void uwb_bce_kfree(struct kref *_bce)
{
	struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt);
@@ -185,10 +179,11 @@ void uwb_bce_kfree(struct kref *_bce)

/* Find a beacon by dev addr in the cache */
static
struct uwb_beca_e *__uwb_beca_find_bydev(const struct uwb_dev_addr *dev_addr)
struct uwb_beca_e *__uwb_beca_find_bydev(struct uwb_rc *rc,
					 const struct uwb_dev_addr *dev_addr)
{
	struct uwb_beca_e *bce, *next;
	list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
	list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
		d_printf(6, NULL, "looking for addr %02x:%02x in %02x:%02x\n",
			 dev_addr->data[0], dev_addr->data[1],
			 bce->dev_addr.data[0], bce->dev_addr.data[1]);
@@ -202,10 +197,11 @@ struct uwb_beca_e *__uwb_beca_find_bydev(const struct uwb_dev_addr *dev_addr)

/* Find a beacon by dev addr in the cache */
static
struct uwb_beca_e *__uwb_beca_find_bymac(const struct uwb_mac_addr *mac_addr)
struct uwb_beca_e *__uwb_beca_find_bymac(struct uwb_rc *rc, 
					 const struct uwb_mac_addr *mac_addr)
{
	struct uwb_beca_e *bce, *next;
	list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
	list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
		if (!memcmp(bce->mac_addr, mac_addr->data,
			    sizeof(struct uwb_mac_addr)))
			goto out;
@@ -229,11 +225,11 @@ struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
	struct uwb_dev *found = NULL;
	struct uwb_beca_e *bce;

	mutex_lock(&uwb_beca.mutex);
	bce = __uwb_beca_find_bydev(devaddr);
	mutex_lock(&rc->uwb_beca.mutex);
	bce = __uwb_beca_find_bydev(rc, devaddr);
	if (bce)
		found = uwb_dev_try_get(rc, bce->uwb_dev);
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);

	return found;
}
@@ -249,11 +245,11 @@ struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
	struct uwb_dev *found = NULL;
	struct uwb_beca_e *bce;

	mutex_lock(&uwb_beca.mutex);
	bce = __uwb_beca_find_bymac(macaddr);
	mutex_lock(&rc->uwb_beca.mutex);
	bce = __uwb_beca_find_bymac(rc, macaddr);
	if (bce)
		found = uwb_dev_try_get(rc, bce->uwb_dev);
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);

	return found;
}
@@ -274,7 +270,9 @@ static void uwb_beca_e_init(struct uwb_beca_e *bce)
 * @bf:         Beacon frame (part of b, really)
 * @ts_jiffies: Timestamp (in jiffies) when the beacon was received
 */
struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be,
static
struct uwb_beca_e *__uwb_beca_add(struct uwb_rc *rc,
				  struct uwb_rc_evt_beacon *be,
				  struct uwb_beacon_frame *bf,
				  unsigned long ts_jiffies)
{
@@ -286,7 +284,7 @@ struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be,
	uwb_beca_e_init(bce);
	bce->ts_jiffies = ts_jiffies;
	bce->uwb_dev = NULL;
	list_add(&bce->node, &uwb_beca.list);
	list_add(&bce->node, &rc->uwb_beca.list);
	return bce;
}

@@ -295,13 +293,13 @@ struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be,
 *
 * Remove associated devicest too.
 */
void uwb_beca_purge(void)
void uwb_beca_purge(struct uwb_rc *rc)
{
	struct uwb_beca_e *bce, *next;
	unsigned long expires;

	mutex_lock(&uwb_beca.mutex);
	list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
	mutex_lock(&rc->uwb_beca.mutex);
	list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
		expires = bce->ts_jiffies + msecs_to_jiffies(beacon_timeout_ms);
		if (time_after(jiffies, expires)) {
			uwbd_dev_offair(bce);
@@ -309,19 +307,20 @@ void uwb_beca_purge(void)
			uwb_bce_put(bce);
		}
	}
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);
}

/* Clean up the whole beacon cache. Called on shutdown */
void uwb_beca_release(void)
void uwb_beca_release(struct uwb_rc *rc)
{
	struct uwb_beca_e *bce, *next;
	mutex_lock(&uwb_beca.mutex);
	list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {

	mutex_lock(&rc->uwb_beca.mutex);
	list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
		list_del(&bce->node);
		uwb_bce_put(bce);
	}
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);
}

static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be,
@@ -437,18 +436,18 @@ int uwbd_evt_handle_rc_beacon(struct uwb_event *evt)
	if (uwb_mac_addr_bcast(&bf->Device_Identifier))
		return 0;

	mutex_lock(&uwb_beca.mutex);
	bce = __uwb_beca_find_bymac(&bf->Device_Identifier);
	mutex_lock(&rc->uwb_beca.mutex);
	bce = __uwb_beca_find_bymac(rc, &bf->Device_Identifier);
	if (bce == NULL) {
		/* Not in there, a new device is pinging */
		uwb_beacon_print(evt->rc, be, bf);
		bce = __uwb_beca_add(be, bf, evt->ts_jiffies);
		bce = __uwb_beca_add(rc, be, bf, evt->ts_jiffies);
		if (bce == NULL) {
			mutex_unlock(&uwb_beca.mutex);
			mutex_unlock(&rc->uwb_beca.mutex);
			return -ENOMEM;
		}
	}
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);

	mutex_lock(&bce->mutex);
	/* purge old beacon data */
+0 −2
Original line number Diff line number Diff line
@@ -118,7 +118,6 @@ static int __init uwb_subsys_init(void)
	result = class_register(&uwb_rc_class);
	if (result < 0)
		goto error_uwb_rc_class_register;
	uwbd_start();
	uwb_dbg_init();
	return 0;

@@ -132,7 +131,6 @@ module_init(uwb_subsys_init);
static void __exit uwb_subsys_exit(void)
{
	uwb_dbg_exit();
	uwbd_stop();
	class_unregister(&uwb_rc_class);
	uwb_est_destroy();
	return;
+14 −7
Original line number Diff line number Diff line
@@ -36,8 +36,6 @@
#include <linux/etherdevice.h>
#include <linux/usb.h>

#define D_LOCAL 1
#include <linux/uwb/debug.h>
#include "uwb-internal.h"

static int uwb_rc_index_match(struct device *dev, void *data)
@@ -83,7 +81,6 @@ static void uwb_rc_sys_release(struct device *dev)

	uwb_rc_neh_destroy(rc);
	uwb_rc_ie_release(rc);
	d_printf(1, dev, "freed uwb_rc %p\n", rc);
	kfree(rc);
}

@@ -100,6 +97,8 @@ void uwb_rc_init(struct uwb_rc *rc)
	rc->scan_type = UWB_SCAN_DISABLED;
	INIT_LIST_HEAD(&rc->notifs_chain.list);
	mutex_init(&rc->notifs_chain.mutex);
	INIT_LIST_HEAD(&rc->uwb_beca.list);
	mutex_init(&rc->uwb_beca.mutex);
	uwb_drp_avail_init(rc);
	uwb_rc_ie_init(rc);
	uwb_rsv_init(rc);
@@ -250,6 +249,12 @@ int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv)

	rc->priv = priv;

	init_waitqueue_head(&rc->uwbd.wq);
	INIT_LIST_HEAD(&rc->uwbd.event_list);
	spin_lock_init(&rc->uwbd.event_list_lock);

	uwbd_start(rc);

	result = rc->start(rc);
	if (result < 0)
		goto error_rc_start;
@@ -284,7 +289,7 @@ int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv)
error_dev_add:
error_rc_setup:
	rc->stop(rc);
	uwbd_flush(rc);
	uwbd_stop(rc);
error_rc_start:
	return result;
}
@@ -315,16 +320,18 @@ void uwb_rc_rm(struct uwb_rc *rc)
	uwb_rc_reset(rc);

	rc->stop(rc);
	uwbd_flush(rc);

	uwbd_stop(rc);

	uwb_dev_lock(&rc->uwb_dev);
	rc->priv = NULL;
	rc->cmd = NULL;
	uwb_dev_unlock(&rc->uwb_dev);
	mutex_lock(&uwb_beca.mutex);
	mutex_lock(&rc->uwb_beca.mutex);
	uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL);
	__uwb_rc_sys_rm(rc);
	mutex_unlock(&uwb_beca.mutex);
	mutex_unlock(&rc->uwb_beca.mutex);
 	uwb_beca_release(rc);
	uwb_dev_rm(&rc->uwb_dev);
}
EXPORT_SYMBOL_GPL(uwb_rc_rm);
+4 −16
Original line number Diff line number Diff line
@@ -160,8 +160,8 @@ struct uwb_event {
	};
};

extern void uwbd_start(void);
extern void uwbd_stop(void);
extern void uwbd_start(struct uwb_rc *rc);
extern void uwbd_stop(struct uwb_rc *rc);
extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask);
extern void uwbd_event_queue(struct uwb_event *);
void uwbd_flush(struct uwb_rc *rc);
@@ -194,15 +194,6 @@ int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt);

extern unsigned long beacon_timeout_ms;

/** Beacon cache list */
struct uwb_beca {
	struct list_head list;
	size_t entries;
	struct mutex mutex;
};

extern struct uwb_beca uwb_beca;

/**
 * Beacon cache entry
 *
@@ -229,9 +220,6 @@ struct uwb_beca_e {
struct uwb_beacon_frame;
extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *,
				 char *, size_t);
extern struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *,
					 struct uwb_beacon_frame *,
					 unsigned long);

extern void uwb_bce_kfree(struct kref *_bce);
static inline void uwb_bce_get(struct uwb_beca_e *bce)
@@ -242,8 +230,8 @@ static inline void uwb_bce_put(struct uwb_beca_e *bce)
{
	kref_put(&bce->refcnt, uwb_bce_kfree);
}
extern void uwb_beca_purge(void);
extern void uwb_beca_release(void);
extern void uwb_beca_purge(struct uwb_rc *rc);
extern void uwb_beca_release(struct uwb_rc *rc);

struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
				       const struct uwb_dev_addr *devaddr);
+32 −66
Original line number Diff line number Diff line
@@ -170,8 +170,6 @@ static const struct uwbd_event uwbd_message_handlers[] = {
	},
};

static DEFINE_MUTEX(uwbd_event_mutex);

/**
 * Handle an URC event passed to the UWB Daemon
 *
@@ -235,19 +233,10 @@ static void uwbd_event_handle_message(struct uwb_event *evt)
		return;
	}

	/* If this is a reset event we need to drop the
	 * uwbd_event_mutex or it deadlocks when the reset handler
	 * attempts to flush the uwbd events. */
	if (evt->message == UWB_EVT_MSG_RESET)
		mutex_unlock(&uwbd_event_mutex);

	result = uwbd_message_handlers[evt->message].handler(evt);
	if (result < 0)
		dev_err(&rc->uwb_dev.dev, "UWBD: '%s' message failed: %d\n",
			uwbd_message_handlers[evt->message].name, result);

	if (evt->message == UWB_EVT_MSG_RESET)
		mutex_lock(&uwbd_event_mutex);
}

static void uwbd_event_handle(struct uwb_event *evt)
@@ -275,20 +264,6 @@ static void uwbd_event_handle(struct uwb_event *evt)

	__uwb_rc_put(rc);	/* for the __uwb_rc_get() in uwb_rc_notif_cb() */
}
/* The UWB Daemon */


/** Daemon's PID: used to decide if we can queue or not */
static int uwbd_pid;
/** Daemon's task struct for managing the kthread */
static struct task_struct *uwbd_task;
/** Daemon's waitqueue for waiting for new events */
static DECLARE_WAIT_QUEUE_HEAD(uwbd_wq);
/** Daemon's list of events; we queue/dequeue here */
static struct list_head uwbd_event_list = LIST_HEAD_INIT(uwbd_event_list);
/** Daemon's list lock to protect concurent access */
static DEFINE_SPINLOCK(uwbd_event_list_lock);


/**
 * UWB Daemon
@@ -302,65 +277,58 @@ static DEFINE_SPINLOCK(uwbd_event_list_lock);
 * FIXME: should change so we don't have a 1HZ timer all the time, but
 *        only if there are devices.
 */
static int uwbd(void *unused)
static int uwbd(void *param)
{
	struct uwb_rc *rc = param;
	unsigned long flags;
	struct list_head list = LIST_HEAD_INIT(list);
	struct uwb_event *evt, *nxt;
	struct uwb_event *evt;
	int should_stop = 0;

	while (1) {
		wait_event_interruptible_timeout(
			uwbd_wq,
			!list_empty(&uwbd_event_list)
			rc->uwbd.wq,
			!list_empty(&rc->uwbd.event_list)
			  || (should_stop = kthread_should_stop()),
			HZ);
		if (should_stop)
			break;
		try_to_freeze();

		mutex_lock(&uwbd_event_mutex);
		spin_lock_irqsave(&uwbd_event_list_lock, flags);
		list_splice_init(&uwbd_event_list, &list);
		spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
		list_for_each_entry_safe(evt, nxt, &list, list_node) {
		spin_lock_irqsave(&rc->uwbd.event_list_lock, flags);
		if (!list_empty(&rc->uwbd.event_list)) {
			evt = list_first_entry(&rc->uwbd.event_list, struct uwb_event, list_node);
			list_del(&evt->list_node);
		} else
			evt = NULL;
		spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags);

		if (evt) {
			uwbd_event_handle(evt);
			kfree(evt);
		}
		mutex_unlock(&uwbd_event_mutex);

		uwb_beca_purge();	/* Purge devices that left */
		uwb_beca_purge(rc);	/* Purge devices that left */
	}
	return 0;
}


/** Start the UWB daemon */
void uwbd_start(void)
void uwbd_start(struct uwb_rc *rc)
{
	uwbd_task = kthread_run(uwbd, NULL, "uwbd");
	if (uwbd_task == NULL)
	rc->uwbd.task = kthread_run(uwbd, rc, "uwbd");
	if (rc->uwbd.task == NULL)
		printk(KERN_ERR "UWB: Cannot start management daemon; "
		       "UWB won't work\n");
	else
		uwbd_pid = uwbd_task->pid;
		rc->uwbd.pid = rc->uwbd.task->pid;
}

/* Stop the UWB daemon and free any unprocessed events */
void uwbd_stop(void)
void uwbd_stop(struct uwb_rc *rc)
{
	unsigned long flags;
	struct uwb_event *evt, *nxt;
	kthread_stop(uwbd_task);
	spin_lock_irqsave(&uwbd_event_list_lock, flags);
	uwbd_pid = 0;
	list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) {
		if (evt->type == UWB_EVT_TYPE_NOTIF)
			kfree(evt->notif.rceb);
		kfree(evt);
	}
	spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
	uwb_beca_release();
	kthread_stop(rc->uwbd.task);
	uwbd_flush(rc);
}

/*
@@ -377,18 +345,20 @@ void uwbd_stop(void)
 */
void uwbd_event_queue(struct uwb_event *evt)
{
	struct uwb_rc *rc = evt->rc;
	unsigned long flags;
	spin_lock_irqsave(&uwbd_event_list_lock, flags);
	if (uwbd_pid != 0) {
		list_add(&evt->list_node, &uwbd_event_list);
		wake_up_all(&uwbd_wq);

	spin_lock_irqsave(&rc->uwbd.event_list_lock, flags);
	if (rc->uwbd.pid != 0) {
		list_add(&evt->list_node, &rc->uwbd.event_list);
		wake_up_all(&rc->uwbd.wq);
	} else {
		__uwb_rc_put(evt->rc);
		if (evt->type == UWB_EVT_TYPE_NOTIF)
			kfree(evt->notif.rceb);
		kfree(evt);
	}
	spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
	spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags);
	return;
}

@@ -396,10 +366,8 @@ void uwbd_flush(struct uwb_rc *rc)
{
	struct uwb_event *evt, *nxt;

	mutex_lock(&uwbd_event_mutex);

	spin_lock_irq(&uwbd_event_list_lock);
	list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) {
	spin_lock_irq(&rc->uwbd.event_list_lock);
	list_for_each_entry_safe(evt, nxt, &rc->uwbd.event_list, list_node) {
		if (evt->rc == rc) {
			__uwb_rc_put(rc);
			list_del(&evt->list_node);
@@ -408,7 +376,5 @@ void uwbd_flush(struct uwb_rc *rc)
			kfree(evt);
		}
	}
	spin_unlock_irq(&uwbd_event_list_lock);

	mutex_unlock(&uwbd_event_mutex);
	spin_unlock_irq(&rc->uwbd.event_list_lock);
}
Loading