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

Commit 66d639a7 authored by Eric Holmberg's avatar Eric Holmberg Committed by Gerrit - the friendly Code Review server
Browse files

soc: qcom: glink: add read/write reference lock



Some internal data structures can be freed in several different code
paths depending upon their state.  To reduce the likelihood of memory
leaks, these structures are reference counted.  In addition, some code
paths will read from the objects and others will write to them.  To
minimize the amount of time in atomic context, the reference counts can
be classified as either reader or writer which allows multiple readers
to hold the lock at a single time.

Combine a reference lock with read/write lock to handle this use case.

Change-Id: Icaf5d0dd639422582dcdd80acb54a342fc0f1c3c
Signed-off-by: default avatarEric Holmberg <eholmber@codeaurora.org>
parent 3570a3f9
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@

/**
 * struct glink_core_xprt_ctx - transport representation structure
 * @xprt_state_lhb0:		controls read/write access to transport state
 * @list_node:			used to chain this transport in a global
 *				transport list
 * @name:			name of this transport
@@ -60,6 +61,7 @@
 * @tx_ready:			list of all channels ready to transmit
 */
struct glink_core_xprt_ctx {
	struct rwref_lock xprt_state_lhb0;
	struct list_head list_node;
	char name[GLINK_NAME_SIZE];
	char edge[GLINK_NAME_SIZE];
@@ -87,6 +89,7 @@ struct glink_core_xprt_ctx {

/**
 * Channel Context
 * @xprt_state_lhb0:	controls read/write access to channel state
 * @port_list_node:	channel list node used by transport "channels" list
 * @tx_ready_list_node:	channels that have data ready to transmit
 * @name:		name of the channel
@@ -126,6 +129,7 @@ struct glink_core_xprt_ctx {
 * @rsigs:				Remote signals
 */
struct channel_ctx {
	struct rwref_lock ch_state_lhc0;
	struct list_head port_list_node;
	struct list_head tx_ready_list_node;
	char name[GLINK_NAME_SIZE];
+195 −0
Original line number Diff line number Diff line
@@ -604,4 +604,199 @@ void glink_get_ch_intent_info(struct channel_ctx *ch_ctx,
 */
int glink_ssr(const char *subsystem);

/**
 * struct rwref_lock - Read/Write Reference Lock
 *
 * kref:	reference count
 * read_count:	number of readers that own the lock
 * write_count:	number of writers (max 1) that own the lock
 * count_zero:	used for internal signaling for non-atomic locks
 *
 * A Read/Write Reference Lock is a combination of a read/write spinlock and a
 * refence count.  The main difference is that no locks are held in the
 * critical section and the lifetime of the object is guaranteed.
 *
 * Read Locking
 * Multiple readers may access the lock at any given time and a read lock will
 * also ensure that the object exists for the life of the lock.
 *
 * rwref_read_get()
 *     use resource in "critical section" (no locks are held)
 * rwref_read_put()
 *
 * Write Locking
 * A single writer may access the lock at any given time and a write lock will
 * also ensure that the object exists for the life of the lock.
 *
 * rwref_write_get()
 *     use resource in "critical section" (no locks are held)
 * rwref_write_put()
 *
 * Reference Lock
 * To ensure the lifetime of the lock (and not affect the read or write lock),
 * a simple reference can be done.  By default, rwref_lock_init() will set the
 * reference count to 1.
 *
 * rwref_lock_init()  Reference count is 1
 * rwref_get()        Reference count is 2
 * rwref_put()        Reference count is 1
 * rwref_put()        Reference count goes to 0 and object is destroyed
 */
struct rwref_lock {
	struct kref kref;
	unsigned read_count;
	unsigned write_count;
	spinlock_t lock;
	struct completion count_zero;

	void (*release)(struct rwref_lock *);
};

/**
 * rwref_lock_release() - Initialize rwref_lock
 * lock_ptr:	pointer to lock structure
 */
static inline void rwref_lock_release(struct kref *kref_ptr)
{
	struct rwref_lock *lock_ptr;

	BUG_ON(kref_ptr == NULL);

	lock_ptr = container_of(kref_ptr, struct rwref_lock, kref);
	if (lock_ptr->release)
		lock_ptr->release(lock_ptr);
}

/**
 * rwref_lock_init() - Initialize rwref_lock
 * lock_ptr:	pointer to lock structure
 * release:	release function called when reference count goes to 0
 */
static inline void rwref_lock_init(struct rwref_lock *lock_ptr,
		void (*release)(struct rwref_lock *))
{
	BUG_ON(lock_ptr == NULL);

	kref_init(&lock_ptr->kref);
	lock_ptr->read_count = 0;
	lock_ptr->write_count = 0;
	spin_lock_init(&lock_ptr->lock);
	init_completion(&lock_ptr->count_zero);
	lock_ptr->release = release;
}

/**
 * rwref_get() - gains a reference count for the object
 * lock_ptr:	pointer to lock structure
 */
static inline void rwref_get(struct rwref_lock *lock_ptr)
{
	BUG_ON(lock_ptr == NULL);

	kref_get(&lock_ptr->kref);
}

/**
 * rwref_put() - puts a reference count for the object
 * lock_ptr:	pointer to lock structure
 *
 * If the reference count goes to zero, the release function is called.
 */
static inline void rwref_put(struct rwref_lock *lock_ptr)
{
	BUG_ON(lock_ptr == NULL);

	kref_put(&lock_ptr->kref, rwref_lock_release);
}

/**
 * rwref_read_get() - gains a reference count for a read operation
 * lock_ptr:	pointer to lock structure
 *
 * Multiple readers may acquire the lock as long as the write count is zero.
 */
static inline void rwref_read_get(struct rwref_lock *lock_ptr)
{
	unsigned long flags;

	BUG_ON(lock_ptr == NULL);

	kref_get(&lock_ptr->kref);
	while (1) {
		spin_lock_irqsave(&lock_ptr->lock, flags);
		if (lock_ptr->write_count == 0) {
			lock_ptr->read_count++;
			spin_unlock_irqrestore(&lock_ptr->lock, flags);
			break;
		}
		spin_unlock_irqrestore(&lock_ptr->lock, flags);
		wait_for_completion(&lock_ptr->count_zero);
	}
}

/**
 * rwref_read_put() - returns a reference count for a read operation
 * lock_ptr:	pointer to lock structure
 *
 * Must be preceded by a call to rwref_read_get().
 */
static inline void rwref_read_put(struct rwref_lock *lock_ptr)
{
	unsigned long flags;

	BUG_ON(lock_ptr == NULL);

	spin_lock_irqsave(&lock_ptr->lock, flags);
	BUG_ON(lock_ptr->read_count == 0);
	if (--lock_ptr->read_count == 0)
		complete(&lock_ptr->count_zero);
	spin_unlock_irqrestore(&lock_ptr->lock, flags);
	kref_put(&lock_ptr->kref, rwref_lock_release);
}

/**
 * rwref_write_get() - gains a reference count for a write operation
 * lock_ptr:	pointer to lock structure
 *
 * Only one writer may acquire the lock as long as the reader count is zero.
 */
static inline void rwref_write_get(struct rwref_lock *lock_ptr)
{
	unsigned long flags;

	BUG_ON(lock_ptr == NULL);

	kref_get(&lock_ptr->kref);
	while (1) {
		spin_lock_irqsave(&lock_ptr->lock, flags);
		if (lock_ptr->read_count == 0 && lock_ptr->write_count == 0) {
			lock_ptr->write_count++;
			spin_unlock_irqrestore(&lock_ptr->lock, flags);
			break;
		}
		spin_unlock_irqrestore(&lock_ptr->lock, flags);
		wait_for_completion(&lock_ptr->count_zero);
	}
}

/**
 * rwref_write_put() - returns a reference count for a write operation
 * lock_ptr:	pointer to lock structure
 *
 * Must be preceded by a call to rwref_write_get().
 */
static inline void rwref_write_put(struct rwref_lock *lock_ptr)
{
	unsigned long flags;

	BUG_ON(lock_ptr == NULL);

	spin_lock_irqsave(&lock_ptr->lock, flags);
	BUG_ON(lock_ptr->write_count != 1);
	if (--lock_ptr->write_count == 0)
		complete(&lock_ptr->count_zero);
	spin_unlock_irqrestore(&lock_ptr->lock, flags);
	kref_put(&lock_ptr->kref, rwref_lock_release);
}

#endif /* _SOC_QCOM_GLINK_PRIVATE_H_ */