Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

genlock: update to ics_chocolate

  • Loading branch information...
commit af2d1a6f740bd6895bfca20bde81cf8d178b8782 1 parent 047ea17
@KonstaT authored
Showing with 148 additions and 34 deletions.
  1. +142 −33 drivers/base/genlock.c
  2. +6 −1 include/linux/genlock.h
View
175 drivers/base/genlock.c
@@ -34,7 +34,15 @@
#define GENLOCK_LOG_ERR(fmt, args...) \
pr_err("genlock: %s: " fmt, __func__, ##args)
+/* The genlock magic stored in the kernel private data is used to protect
+ * against the possibility of user space passing a valid fd to a
+ * non-genlock file for genlock_attach_lock()
+ */
+#define GENLOCK_MAGIC_OK 0xD2EAD10C
+#define GENLOCK_MAGIC_BAD 0xD2EADBAD
+
struct genlock {
+ unsigned int magic; /* Magic for attach verification */
struct list_head active; /* List of handles holding lock */
spinlock_t lock; /* Spinlock to protect the lock internals */
wait_queue_head_t queue; /* Holding pen for processes pending lock */
@@ -56,7 +64,7 @@ struct genlock_handle {
* released while another process tries to attach it
*/
-static DEFINE_SPINLOCK(genlock_file_lock);
+static DEFINE_SPINLOCK(genlock_ref_lock);
static void genlock_destroy(struct kref *kref)
{
@@ -68,10 +76,9 @@ static void genlock_destroy(struct kref *kref)
* still active after the lock gets released
*/
- spin_lock(&genlock_file_lock);
if (lock->file)
lock->file->private_data = NULL;
- spin_unlock(&genlock_file_lock);
+ lock->magic = GENLOCK_MAGIC_BAD;
kfree(lock);
}
@@ -130,6 +137,7 @@ struct genlock *genlock_create_lock(struct genlock_handle *handle)
init_waitqueue_head(&lock->queue);
spin_lock_init(&lock->lock);
+ lock->magic = GENLOCK_MAGIC_OK;
lock->state = _UNLOCKED;
/*
@@ -203,21 +211,30 @@ struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd)
* released and then attached
*/
- spin_lock(&genlock_file_lock);
+ spin_lock(&genlock_ref_lock);
lock = file->private_data;
- spin_unlock(&genlock_file_lock);
fput(file);
if (lock == NULL) {
GENLOCK_LOG_ERR("File descriptor is invalid\n");
- return ERR_PTR(-EINVAL);
+ goto fail_invalid;
+ }
+
+ if (lock->magic != GENLOCK_MAGIC_OK) {
+ GENLOCK_LOG_ERR("Magic is invalid - 0x%X\n", lock->magic);
+ goto fail_invalid;
}
handle->lock = lock;
kref_get(&lock->refcount);
+ spin_unlock(&genlock_ref_lock);
return lock;
+
+fail_invalid:
+ spin_unlock(&genlock_ref_lock);
+ return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL(genlock_attach_lock);
@@ -307,12 +324,15 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
if (handle_has_lock(lock, handle)) {
/*
- * If the handle already holds the lock and the type matches,
- * then just increment the active pointer. This allows the
- * handle to do recursive locks
+ * If the handle already holds the lock and the lock type is
+ * a read lock then just increment the active pointer. This
+ * allows the handle to do recursive read locks. Recursive
+ * write locks are not allowed in order to support
+ * synchronization within a process using a single gralloc
+ * handle.
*/
- if (lock->state == op) {
+ if (lock->state == _RDLOCK && op == _RDLOCK) {
handle->active++;
goto done;
}
@@ -321,32 +341,45 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
* If the handle holds a write lock then the owner can switch
* to a read lock if they want. Do the transition atomically
* then wake up any pending waiters in case they want a read
- * lock too.
+ * lock too. In order to support synchronization within a
+ * process the caller must explicity request to convert the
+ * lock type with the GENLOCK_WRITE_TO_READ flag.
*/
- if (op == _RDLOCK && handle->active == 1) {
- lock->state = _RDLOCK;
- wake_up(&lock->queue);
- goto done;
+ if (flags & GENLOCK_WRITE_TO_READ) {
+ if (lock->state == _WRLOCK && op == _RDLOCK) {
+ lock->state = _RDLOCK;
+ wake_up(&lock->queue);
+ goto done;
+ } else {
+ GENLOCK_LOG_ERR("Invalid state to convert"
+ "write to read\n");
+ ret = -EINVAL;
+ goto done;
+ }
}
+ } else {
/*
- * Otherwise the user tried to turn a read into a write, and we
- * don't allow that.
+ * Check to ensure the caller has not attempted to convert a
+ * write to a read without holding the lock.
*/
- GENLOCK_LOG_ERR("Trying to upgrade a read lock to a write"
- "lock\n");
- ret = -EINVAL;
- goto done;
- }
- /*
- * If we request a read and the lock is held by a read, then go
- * ahead and share the lock
- */
+ if (flags & GENLOCK_WRITE_TO_READ) {
+ GENLOCK_LOG_ERR("Handle must have lock to convert"
+ "write to read\n");
+ ret = -EINVAL;
+ goto done;
+ }
- if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
- goto dolock;
+ /*
+ * If we request a read and the lock is held by a read, then go
+ * ahead and share the lock
+ */
+
+ if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
+ goto dolock;
+ }
/* Treat timeout 0 just like a NOBLOCK flag and return if the
lock cannot be aquired without blocking */
@@ -356,15 +389,26 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
goto done;
}
- /* Wait while the lock remains in an incompatible state */
+ /*
+ * Wait while the lock remains in an incompatible state
+ * state op wait
+ * -------------------
+ * unlocked n/a no
+ * read read no
+ * read write yes
+ * write n/a yes
+ */
- while (lock->state != _UNLOCKED) {
+ while ((lock->state == _RDLOCK && op == _WRLOCK) ||
+ lock->state == _WRLOCK) {
signed long elapsed;
spin_unlock_irqrestore(&lock->lock, irqflags);
elapsed = wait_event_interruptible_timeout(lock->queue,
- lock->state == _UNLOCKED, ticks);
+ lock->state == _UNLOCKED ||
+ (lock->state == _RDLOCK && op == _RDLOCK),
+ ticks);
spin_lock_irqsave(&lock->lock, irqflags);
@@ -381,7 +425,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
list_add_tail(&handle->entry, &lock->active);
lock->state = op;
- handle->active = 1;
+ handle->active++;
done:
spin_unlock_irqrestore(&lock->lock, irqflags);
@@ -390,7 +434,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
}
/**
- * genlock_lock - Acquire or release a lock
+ * genlock_lock - Acquire or release a lock (depreciated)
* @handle - pointer to the genlock handle that is requesting the lock
* @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
* @flags - flags to control the operation
@@ -403,6 +447,7 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags,
uint32_t timeout)
{
struct genlock *lock;
+ unsigned long irqflags;
int ret = 0;
@@ -423,6 +468,13 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags,
ret = _genlock_unlock(lock, handle);
break;
case GENLOCK_RDLOCK:
+ spin_lock_irqsave(&lock->lock, irqflags);
+ if (handle_has_lock(lock, handle)) {
+ /* request the WRITE_TO_READ flag for compatibility */
+ flags |= GENLOCK_WRITE_TO_READ;
+ }
+ spin_unlock_irqrestore(&lock->lock, irqflags);
+ /* fall through to take lock */
case GENLOCK_WRLOCK:
ret = _genlock_lock(lock, handle, op, flags, timeout);
break;
@@ -437,6 +489,53 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags,
EXPORT_SYMBOL(genlock_lock);
/**
+ * genlock_dreadlock - Acquire or release a lock
+ * @handle - pointer to the genlock handle that is requesting the lock
+ * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
+ * @flags - flags to control the operation
+ * @timeout - optional timeout to wait for the lock to come free
+ *
+ * Returns: 0 on success or error code on failure
+ */
+
+int genlock_dreadlock(struct genlock_handle *handle, int op, int flags,
+ uint32_t timeout)
+{
+ struct genlock *lock;
+
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(handle)) {
+ GENLOCK_LOG_ERR("Invalid handle\n");
+ return -EINVAL;
+ }
+
+ lock = handle->lock;
+
+ if (lock == NULL) {
+ GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
+ return -EINVAL;
+ }
+
+ switch (op) {
+ case GENLOCK_UNLOCK:
+ ret = _genlock_unlock(lock, handle);
+ break;
+ case GENLOCK_RDLOCK:
+ case GENLOCK_WRLOCK:
+ ret = _genlock_lock(lock, handle, op, flags, timeout);
+ break;
+ default:
+ GENLOCK_LOG_ERR("Invalid lock operation\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(genlock_dreadlock);
+
+/**
* genlock_wait - Wait for the lock to be released
* @handle - pointer to the genlock handle that is waiting for the lock
* @timeout - optional timeout to wait for the lock to get released
@@ -513,7 +612,9 @@ static void genlock_release_lock(struct genlock_handle *handle)
}
spin_unlock_irqrestore(&handle->lock->lock, flags);
+ spin_lock(&genlock_ref_lock);
kref_put(&handle->lock->refcount, genlock_destroy);
+ spin_unlock(&genlock_ref_lock);
handle->lock = NULL;
handle->active = 0;
}
@@ -657,6 +758,14 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd,
return genlock_lock(handle, param.op, param.flags,
param.timeout);
}
+ case GENLOCK_IOC_DREADLOCK: {
+ if (copy_from_user(&param, (void __user *) arg,
+ sizeof(param)))
+ return -EFAULT;
+
+ return genlock_dreadlock(handle, param.op, param.flags,
+ param.timeout);
+ }
case GENLOCK_IOC_WAIT: {
if (copy_from_user(&param, (void __user *) arg,
sizeof(param)))
View
7 include/linux/genlock.h
@@ -21,7 +21,8 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags,
#define GENLOCK_WRLOCK 1
#define GENLOCK_RDLOCK 2
-#define GENLOCK_NOBLOCK (1 << 0)
+#define GENLOCK_NOBLOCK (1 << 0)
+#define GENLOCK_WRITE_TO_READ (1 << 1)
struct genlock_lock {
int fd;
@@ -37,6 +38,8 @@ struct genlock_lock {
struct genlock_lock)
#define GENLOCK_IOC_ATTACH _IOW(GENLOCK_IOC_MAGIC, 2, \
struct genlock_lock)
+
+/* Deprecated */
#define GENLOCK_IOC_LOCK _IOW(GENLOCK_IOC_MAGIC, 3, \
struct genlock_lock)
@@ -44,4 +47,6 @@ struct genlock_lock {
#define GENLOCK_IOC_RELEASE _IO(GENLOCK_IOC_MAGIC, 4)
#define GENLOCK_IOC_WAIT _IOW(GENLOCK_IOC_MAGIC, 5, \
struct genlock_lock)
+#define GENLOCK_IOC_DREADLOCK _IOW(GENLOCK_IOC_MAGIC, 6, \
+ struct genlock_lock)
#endif
Please sign in to comment.
Something went wrong with that request. Please try again.