Merge "msm: mdss: enable MRPS command for short read commands"
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 478e766..b9f0f8b 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -503,6 +503,7 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_PFT=y
CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index c5c16c2..e843739 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -561,6 +561,7 @@
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_PFT=y
CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_MSM_RDBG=m
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 588c243..5f80fcf 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -155,6 +155,7 @@
struct adreno_context *drawctxt)
{
struct kgsl_cmdbatch *cmdbatch = NULL;
+ int pending;
mutex_lock(&drawctxt->mutex);
if (drawctxt->cmdqueue_head != drawctxt->cmdqueue_tail) {
@@ -164,7 +165,32 @@
* Don't dequeue a cmdbatch that is still waiting for other
* events
*/
- if (kgsl_cmdbatch_sync_pending(cmdbatch)) {
+
+ spin_lock(&cmdbatch->lock);
+ pending = list_empty(&cmdbatch->synclist) ? 0 : 1;
+
+ /*
+ * If changes are pending and the canary timer hasn't been
+ * started yet, start it
+ */
+ if (pending) {
+ /*
+ * If syncpoints are pending start the canary timer if
+ * it hasn't already been started
+ */
+ if (!timer_pending(&cmdbatch->timer))
+ mod_timer(&cmdbatch->timer, jiffies + (5 * HZ));
+ spin_unlock(&cmdbatch->lock);
+ } else {
+ /*
+ * Otherwise, delete the timer to make sure it is good
+ * and dead before queuing the buffer
+ */
+ spin_unlock(&cmdbatch->lock);
+ del_timer_sync(&cmdbatch->timer);
+ }
+
+ if (pending) {
cmdbatch = ERR_PTR(-EAGAIN);
goto done;
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5d78879..462e7a5 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -105,52 +105,108 @@
}
EXPORT_SYMBOL(kgsl_trace_regwrite);
-int kgsl_memfree_hist_init(void)
-{
- void *base;
+/*
+ * The memfree list contains the last N blocks of memory that have been freed.
+ * On a GPU fault we walk the list to see if the faulting address had been
+ * recently freed and print out a message to that effect
+ */
- base = kzalloc(KGSL_MEMFREE_HIST_SIZE, GFP_KERNEL);
- kgsl_driver.memfree_hist.base_hist_rb = base;
- if (base == NULL)
- return -ENOMEM;
- kgsl_driver.memfree_hist.size = KGSL_MEMFREE_HIST_SIZE;
- kgsl_driver.memfree_hist.wptr = base;
+#define MEMFREE_ENTRIES 512
+
+static DEFINE_SPINLOCK(memfree_lock);
+
+struct memfree_entry {
+ unsigned long gpuaddr;
+ unsigned long size;
+ pid_t pid;
+ unsigned int flags;
+};
+
+static struct {
+ struct memfree_entry *list;
+ int head;
+ int tail;
+} memfree;
+
+static int kgsl_memfree_init(void)
+{
+ memfree.list = kzalloc(MEMFREE_ENTRIES * sizeof(struct memfree_entry),
+ GFP_KERNEL);
+
+ return (memfree.list) ? 0 : -ENOMEM;
+}
+
+static void kgsl_memfree_exit(void)
+{
+ kfree(memfree.list);
+ memset(&memfree, 0, sizeof(memfree));
+}
+
+int kgsl_memfree_find_entry(pid_t pid, unsigned long *gpuaddr,
+ unsigned long *size, unsigned int *flags)
+{
+ int ptr;
+
+ if (memfree.list == NULL)
+ return 0;
+
+ spin_lock(&memfree_lock);
+
+ ptr = memfree.head - 1;
+ if (ptr < 0)
+ ptr = MEMFREE_ENTRIES - 1;
+
+ /* Walk backwards through the list looking for the last match */
+ while (ptr != memfree.tail) {
+ struct memfree_entry *entry = &memfree.list[ptr];
+
+ if ((entry->pid == pid) &&
+ (*gpuaddr >= entry->gpuaddr &&
+ *gpuaddr < (entry->gpuaddr + entry->size))) {
+ *gpuaddr = entry->gpuaddr;
+ *flags = entry->flags;
+ *size = entry->size;
+
+ spin_unlock(&memfree_lock);
+ return 1;
+ }
+
+ ptr = ptr - 1;
+
+ if (ptr < 0)
+ ptr = MEMFREE_ENTRIES - 1;
+ }
+
+ spin_unlock(&memfree_lock);
return 0;
}
-void kgsl_memfree_hist_exit(void)
+static void kgsl_memfree_add(pid_t pid, unsigned int gpuaddr,
+ unsigned int size, int flags)
+
{
- kfree(kgsl_driver.memfree_hist.base_hist_rb);
- kgsl_driver.memfree_hist.base_hist_rb = NULL;
-}
+ struct memfree_entry *entry;
-void kgsl_memfree_hist_set_event(unsigned int pid, unsigned int gpuaddr,
- unsigned int size, int flags)
-{
- struct kgsl_memfree_hist_elem *p;
-
- void *base = kgsl_driver.memfree_hist.base_hist_rb;
- int rbsize = kgsl_driver.memfree_hist.size;
-
- if (base == NULL)
+ if (memfree.list == NULL)
return;
- mutex_lock(&kgsl_driver.memfree_hist_mutex);
- p = kgsl_driver.memfree_hist.wptr;
- p->pid = pid;
- p->gpuaddr = gpuaddr;
- p->size = size;
- p->flags = flags;
+ spin_lock(&memfree_lock);
- kgsl_driver.memfree_hist.wptr++;
- if ((void *)kgsl_driver.memfree_hist.wptr >= base+rbsize) {
- kgsl_driver.memfree_hist.wptr =
- (struct kgsl_memfree_hist_elem *)base;
- }
- mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+ entry = &memfree.list[memfree.head];
+
+ entry->pid = pid;
+ entry->gpuaddr = gpuaddr;
+ entry->size = size;
+ entry->flags = flags;
+
+ memfree.head = (memfree.head + 1) % MEMFREE_ENTRIES;
+
+ if (memfree.head == memfree.tail)
+ memfree.tail = (memfree.tail + 1) % MEMFREE_ENTRIES;
+
+ spin_unlock(&memfree_lock);
}
-
/* kgsl_get_mem_entry - get the mem_entry structure for the specified object
* @device - Pointer to the device structure
* @ptbase - the pagetable base of the object
@@ -557,6 +613,13 @@
write_lock(&device->context_lock);
if (context->id != KGSL_CONTEXT_INVALID) {
+
+ /* Clear the timestamps in the memstore during destroy */
+ kgsl_sharedmem_writel(device, &device->memstore,
+ KGSL_MEMSTORE_OFFSET(context->id, soptimestamp), 0);
+ kgsl_sharedmem_writel(device, &device->memstore,
+ KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp), 0);
+
idr_remove(&device->context_idr, context->id);
context->id = KGSL_CONTEXT_INVALID;
}
@@ -1484,6 +1547,49 @@
struct kref refcount;
};
+static void _kgsl_cmdbatch_timer(unsigned long data)
+{
+ struct kgsl_cmdbatch *cmdbatch = (struct kgsl_cmdbatch *) data;
+ struct kgsl_cmdbatch_sync_event *event;
+
+ if (cmdbatch == NULL || cmdbatch->context == NULL)
+ return;
+
+ spin_lock(&cmdbatch->lock);
+ if (list_empty(&cmdbatch->synclist))
+ goto done;
+
+ pr_err("kgsl: possible gpu syncpoint deadlock for context %d timestamp %d\n",
+ cmdbatch->context->id, cmdbatch->timestamp);
+ pr_err(" Active sync points:\n");
+
+ /* Print all the pending sync objects */
+ list_for_each_entry(event, &cmdbatch->synclist, node) {
+
+ switch (event->type) {
+ case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP: {
+ unsigned int retired;
+
+ retired = kgsl_readtimestamp(event->device,
+ event->context, KGSL_TIMESTAMP_RETIRED);
+
+ pr_err(" [timestamp] context %d timestamp %d (retired %d)\n",
+ event->context->id, event->timestamp,
+ retired);
+ break;
+ }
+ case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
+ pr_err(" fence: [%p] %s\n", event->handle,
+ (event->handle && event->handle->fence)
+ ? event->handle->fence->name : "NULL");
+ break;
+ }
+ }
+
+done:
+ spin_unlock(&cmdbatch->lock);
+}
+
/**
* kgsl_cmdbatch_sync_event_destroy() - Destroy a sync event object
* @kref: Pointer to the kref structure for this object
@@ -1558,6 +1664,10 @@
sched = list_empty(&event->cmdbatch->synclist) ? 1 : 0;
spin_unlock(&event->cmdbatch->lock);
+ /* If the list is empty delete the canary timer */
+ if (sched)
+ del_timer_sync(&event->cmdbatch->timer);
+
/*
* if this is the last event in the list then tell
* the GPU device that the cmdbatch can be submitted
@@ -1600,10 +1710,12 @@
struct kgsl_cmdbatch_sync_event *event, *tmp;
LIST_HEAD(cancel_synclist);
- /*
- * Empty the synclist before canceling events
- */
+ /* Zap the canary timer */
+ del_timer_sync(&cmdbatch->timer);
+
spin_lock(&cmdbatch->lock);
+
+ /* Empty the synclist before canceling events */
list_splice_init(&cmdbatch->synclist, &cancel_synclist);
spin_unlock(&cmdbatch->lock);
@@ -1782,6 +1894,7 @@
event->cmdbatch = cmdbatch;
event->context = context;
event->timestamp = sync->timestamp;
+ event->device = device;
/*
* Two krefs are required to support events. The first kref is for
@@ -1917,6 +2030,10 @@
cmdbatch->context = context;
cmdbatch->flags = flags & ~KGSL_CONTEXT_SUBMIT_IB_LIST;
+ /* Add a timer to help debug sync deadlocks */
+ setup_timer(&cmdbatch->timer, _kgsl_cmdbatch_timer,
+ (unsigned long) cmdbatch);
+
return cmdbatch;
}
@@ -2306,7 +2423,30 @@
return result;
}
-static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
+static long _sharedmem_free_entry(struct kgsl_mem_entry *entry)
+{
+ if (!kgsl_mem_entry_set_pend(entry)) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
+ trace_kgsl_mem_free(entry);
+
+ kgsl_memfree_add(entry->priv->pid, entry->memdesc.gpuaddr,
+ entry->memdesc.size, entry->memdesc.flags);
+
+ /*
+ * First kgsl_mem_entry_put is for the reference that we took in
+ * this function when calling kgsl_sharedmem_find, second one is
+ * to free the memory since this is a free ioctl
+ */
+ kgsl_mem_entry_put(entry);
+ kgsl_mem_entry_put(entry);
+
+ return 0;
+}
+
+long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_sharedmem_free *param = data;
@@ -2319,29 +2459,11 @@
param->gpuaddr);
return -EINVAL;
}
- if (!kgsl_mem_entry_set_pend(entry)) {
- kgsl_mem_entry_put(entry);
- return -EBUSY;
- }
- trace_kgsl_mem_free(entry);
-
- kgsl_memfree_hist_set_event(entry->priv->pid,
- entry->memdesc.gpuaddr,
- entry->memdesc.size,
- entry->memdesc.flags);
-
- /*
- * First kgsl_mem_entry_put is for the reference that we took in
- * this function when calling kgsl_sharedmem_find, second one is
- * to free the memory since this is a free ioctl
- */
- kgsl_mem_entry_put(entry);
- kgsl_mem_entry_put(entry);
- return 0;
+ return _sharedmem_free_entry(entry);
}
-static long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
+long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_gpumem_free_id *param = data;
@@ -2355,26 +2477,7 @@
return -EINVAL;
}
- if (!kgsl_mem_entry_set_pend(entry)) {
- kgsl_mem_entry_put(entry);
- return -EBUSY;
- }
-
- trace_kgsl_mem_free(entry);
-
- kgsl_memfree_hist_set_event(entry->priv->pid,
- entry->memdesc.gpuaddr,
- entry->memdesc.size,
- entry->memdesc.flags);
-
- /*
- * First kgsl_mem_entry_put is for the reference that we took in
- * this function when calling kgsl_sharedmem_find_id, second one is
- * to free the memory since this is a free ioctl
- */
- kgsl_mem_entry_put(entry);
- kgsl_mem_entry_put(entry);
- return 0;
+ return _sharedmem_free_entry(entry);
}
static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr)
@@ -3902,8 +4005,6 @@
.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
- .memfree_hist_mutex =
- __MUTEX_INITIALIZER(kgsl_driver.memfree_hist_mutex),
/*
* Full cache flushes are faster than line by line on at least
* 8064 and 8974 once the region to be flushed is > 16mb.
@@ -4247,7 +4348,7 @@
kgsl_driver.class = NULL;
}
- kgsl_memfree_hist_exit();
+ kgsl_memfree_exit();
unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
}
@@ -4319,8 +4420,7 @@
goto err;
}
- if (kgsl_memfree_hist_init())
- KGSL_CORE_ERR("failed to init memfree_hist");
+ kgsl_memfree_init();
return 0;
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 6da4a86..0bd71cb 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -75,25 +75,8 @@
#define KGSL_STATS_ADD(_size, _stat, _max) \
do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0)
-
-#define KGSL_MEMFREE_HIST_SIZE ((int)(PAGE_SIZE * 2))
-
#define KGSL_MAX_NUMIBS 100000
-struct kgsl_memfree_hist_elem {
- unsigned int pid;
- unsigned int gpuaddr;
- unsigned int size;
- unsigned int flags;
-};
-
-struct kgsl_memfree_hist {
- void *base_hist_rb;
- unsigned int size;
- struct kgsl_memfree_hist_elem *wptr;
-};
-
-
struct kgsl_device;
struct kgsl_context;
@@ -122,9 +105,6 @@
void *ptpool;
- struct mutex memfree_hist_mutex;
- struct kgsl_memfree_hist memfree_hist;
-
struct {
unsigned int vmalloc;
unsigned int vmalloc_max;
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 5645628..ccb2312 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -123,52 +123,6 @@
KGSL_DEBUGFS_LOG(mem_log);
KGSL_DEBUGFS_LOG(pwr_log);
-static int memfree_hist_print(struct seq_file *s, void *unused)
-{
- void *base = kgsl_driver.memfree_hist.base_hist_rb;
-
- struct kgsl_memfree_hist_elem *wptr = kgsl_driver.memfree_hist.wptr;
- struct kgsl_memfree_hist_elem *p;
- char str[16];
-
- seq_printf(s, "%8s %8s %8s %11s\n",
- "pid", "gpuaddr", "size", "flags");
-
- mutex_lock(&kgsl_driver.memfree_hist_mutex);
- p = wptr;
- for (;;) {
- kgsl_get_memory_usage(str, sizeof(str), p->flags);
- /*
- * if the ring buffer is not filled up yet
- * all its empty elems have size==0
- * just skip them ...
- */
- if (p->size)
- seq_printf(s, "%8d %08x %8d %11s\n",
- p->pid, p->gpuaddr, p->size, str);
- p++;
- if ((void *)p >= base + kgsl_driver.memfree_hist.size)
- p = (struct kgsl_memfree_hist_elem *) base;
-
- if (p == kgsl_driver.memfree_hist.wptr)
- break;
- }
- mutex_unlock(&kgsl_driver.memfree_hist_mutex);
- return 0;
-}
-
-static int memfree_hist_open(struct inode *inode, struct file *file)
-{
- return single_open(file, memfree_hist_print, inode->i_private);
-}
-
-static const struct file_operations memfree_hist_fops = {
- .open = memfree_hist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
void kgsl_device_debugfs_init(struct kgsl_device *device)
{
if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir))
@@ -188,8 +142,6 @@
&mem_log_fops);
debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device,
&pwr_log_fops);
- debugfs_create_file("memfree_history", 0444, device->d_debugfs, device,
- &memfree_hist_fops);
/* Create postmortem dump control files */
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 1e6fbc9..6f17d56c 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -173,10 +173,9 @@
* @ibcount: Number of IBs in the command list
* @ibdesc: Pointer to the list of IBs
* @expires: Point in time when the cmdbatch is considered to be hung
- * @invalid: non-zero if the dispatcher determines the command and the owning
- * context should be invalidated
* @refcount: kref structure to maintain the reference count
* @synclist: List of context/timestamp tuples to wait for before issuing
+ * @timer: a timer used to track possible sync timeouts for this cmdbatch
*
* This struture defines an atomic batch of command buffers issued from
* userspace.
@@ -193,9 +192,9 @@
uint32_t ibcount;
struct kgsl_ibdesc *ibdesc;
unsigned long expires;
- int invalid;
struct kref refcount;
struct list_head synclist;
+ struct timer_list timer;
};
/**
@@ -548,6 +547,9 @@
*context);
int kgsl_context_detach(struct kgsl_context *context);
+int kgsl_memfree_find_entry(pid_t pid, unsigned long *gpuaddr,
+ unsigned long *size, unsigned int *flags);
+
/**
* kgsl_context_put() - Release context reference count
* @context: Pointer to the KGSL context to be released
@@ -728,27 +730,6 @@
}
/**
- * kgsl_cmdbatch_sync_pending() - return true if the cmdbatch is waiting
- * @cmdbatch: Pointer to the command batch object to check
- *
- * Return non-zero if the specified command batch is still waiting for sync
- * point dependencies to be satisfied
- */
-static inline int kgsl_cmdbatch_sync_pending(struct kgsl_cmdbatch *cmdbatch)
-{
- int ret;
-
- if (cmdbatch == NULL)
- return 0;
-
- spin_lock(&cmdbatch->lock);
- ret = list_empty(&cmdbatch->synclist) ? 0 : 1;
- spin_unlock(&cmdbatch->lock);
-
- return ret;
-}
-
-/**
* kgsl_sysfs_store() - parse a string from a sysfs store function
* @buf: Incoming string to parse
* @ptr: Pointer to an unsigned int to store the value
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 488e5a8..c4fa8af 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -281,39 +281,20 @@
static void _check_if_freed(struct kgsl_iommu_device *iommu_dev,
unsigned long addr, unsigned int pid)
{
- void *base = kgsl_driver.memfree_hist.base_hist_rb;
- struct kgsl_memfree_hist_elem *wptr;
- struct kgsl_memfree_hist_elem *p;
+ unsigned long gpuaddr = addr;
+ unsigned long size = 0;
+ unsigned int flags = 0;
+
char name[32];
memset(name, 0, sizeof(name));
- mutex_lock(&kgsl_driver.memfree_hist_mutex);
- wptr = kgsl_driver.memfree_hist.wptr;
- p = wptr;
- for (;;) {
- if (p->size && p->pid == pid)
- if (addr >= p->gpuaddr &&
- addr < (p->gpuaddr + p->size)) {
-
- kgsl_get_memory_usage(name, sizeof(name) - 1,
- p->flags);
- KGSL_LOG_DUMP(iommu_dev->kgsldev,
- "---- premature free ----\n");
- KGSL_LOG_DUMP(iommu_dev->kgsldev,
- "[%8.8X-%8.8X] (%s) was already freed by pid %d\n",
- p->gpuaddr,
- p->gpuaddr + p->size,
- name,
- p->pid);
- }
- p++;
- if ((void *)p >= base + kgsl_driver.memfree_hist.size)
- p = (struct kgsl_memfree_hist_elem *) base;
-
- if (p == kgsl_driver.memfree_hist.wptr)
- break;
+ if (kgsl_memfree_find_entry(pid, &gpuaddr, &size, &flags)) {
+ kgsl_get_memory_usage(name, sizeof(name) - 1, flags);
+ KGSL_LOG_DUMP(iommu_dev->kgsldev, "---- premature free ----\n");
+ KGSL_LOG_DUMP(iommu_dev->kgsldev,
+ "[%8.8lX-%8.8lX] (%s) was already freed by pid %d\n",
+ gpuaddr, gpuaddr + size, name, pid);
}
- mutex_unlock(&kgsl_driver.memfree_hist_mutex);
}
static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index dc3ad21..0e694a7 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -118,6 +118,7 @@
struct sync_pt *pt;
struct sync_fence *fence = NULL;
int ret = -EINVAL;
+ char fence_name[sizeof(fence->name)] = {};
if (len != sizeof(priv))
return -EINVAL;
@@ -140,8 +141,13 @@
ret = -ENOMEM;
goto fail_pt;
}
+ snprintf(fence_name, sizeof(fence_name),
+ "%s-pid-%d-ctx-%d-ts-%d",
+ device->name, current->group_leader->pid,
+ context_id, timestamp);
- fence = sync_fence_create("kgsl-fence", pt);
+
+ fence = sync_fence_create(fence_name, pt);
if (fence == NULL) {
/* only destroy pt when not added to fence */
kgsl_sync_pt_destroy(pt);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index b1521df..ebdca89 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -1143,6 +1143,11 @@
vfe_dev->hw_info->vfe_ops.irq_ops.
read_irq_status(vfe_dev, &irq_status0, &irq_status1);
+ if ((irq_status0 == 0) && (irq_status1 == 0)) {
+ pr_err_ratelimited("%s: irq_status0 & 1 are both 0\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
msm_isp_process_overflow_irq(vfe_dev,
&irq_status0, &irq_status1);
vfe_dev->hw_info->vfe_ops.core_ops.
@@ -1158,7 +1163,7 @@
if ((irq_status0 == 0) && (irq_status1 == 0) &&
(!((error_mask0 != 0) || (error_mask1 != 0)) &&
vfe_dev->error_info.error_count == 1)) {
- ISP_DBG("%s: irq_status0 & 1 are both 0!\n", __func__);
+ ISP_DBG("%s: error_mask0/1 & error_count are set!\n", __func__);
return IRQ_HANDLED;
}
@@ -1211,7 +1216,7 @@
spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
if (atomic_read(&vfe_dev->error_info.overflow_state) !=
NO_OVERFLOW) {
- pr_err("There is Overflow, kicking up recovery !!!!");
+ pr_err_ratelimited("There is Overflow, kicking up recovery !!!!");
msm_isp_process_overflow_recovery(vfe_dev,
irq_status0, irq_status1);
continue;
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 88e8d43..11b064a 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -120,6 +120,16 @@
This driver gets the Q6 out of power collapsed state and
exposes ioctl control to read avtimer tick.
+config PFT
+ bool "Per-File-Tagger driver"
+ default n
+ help
+ This driver is used for tagging enterprise files.
+ It is part of the Per-File-Encryption (PFE) feature.
+ The driver is tagging files when created by
+ registered application.
+ Tagged files are encrypted using the dm-req-crypt driver.
+
config SSM
tristate "Qualcomm Secure Service Module"
depends on QSEECOM
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index e6493b1..e2be59d 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -3,7 +3,7 @@
#
ccflags-y += -Idrivers/misc/
-
+ccflags-y += -Isecurity/selinux -Isecurity/selinux/include
obj-$(CONFIG_MSM_SSBI) += ssbi.o
obj-$(CONFIG_USB_BAM) += usb_bam.o
@@ -16,4 +16,5 @@
obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
obj-$(CONFIG_SSM) += ssm.o
+obj-$(CONFIG_PFT) += pft.o
obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
diff --git a/drivers/platform/msm/pft.c b/drivers/platform/msm/pft.c
new file mode 100644
index 0000000..20e7249
--- /dev/null
+++ b/drivers/platform/msm/pft.c
@@ -0,0 +1,1821 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Per-File-Tagger (PFT).
+ *
+ * This driver tags enterprise file for encryption/decryption,
+ * as part of the Per-File-Encryption (PFE) feature.
+ *
+ * Enterprise registered applications are identified by their UID.
+ *
+ * The PFT exposes character-device interface to the user-space application,
+ * to handle the following commands:
+ * 1. Update registered applications list
+ * 2. Encryption (in-place) of a file that was created before.
+ * 3. Set State - update the state.
+ *
+ * The PFT exposes kernel API hooks that are intercepting file operations
+ * like create/open/read/write for tagging files and also for access control.
+ * It utilizes the existing security framework hooks
+ * that calls the selinux hooks.
+ *
+ * The PFT exposes kernel API to the dm-req-crypt driver to provide the info
+ * if a file is tagged or not. The dm-req-crypt driver is doing the
+ * actual encryption/decryptiom.
+ *
+ * Tagging the file:
+ * 1. Non-volatile tagging on storage using file extra-attribute (xattr).
+ * 2. Volatile tagging on the file's inode, for fast access.
+ *
+ */
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+
+#define pr_fmt(fmt) "pft [%s]: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/cred.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+#include <linux/bio.h>
+#include <linux/bitops.h>
+#include <linux/fdtable.h>
+#include <linux/selinux.h>
+
+#include <linux/pft.h>
+#include <linux/msm_pft.h>
+
+#include "objsec.h"
+
+/* File tagging as encrypted/non-encrypted is valid */
+#define PFT_TAG_MAGIC ((u32)(0xABC00000))
+
+/* File tagged as encrypted */
+#define PFT_TAG_ENCRYPTED BIT(16)
+
+#define PFT_TAG_MAGIC_MASK 0xFFF00000
+#define PFT_TAG_FLAGS_MASK 0x000F0000
+#define PFT_TAG_KEY_MASK 0x0000FFFF
+
+/* The defualt encryption key index */
+#define PFT_DEFAULT_KEY_INDEX 1
+
+/* The defualt key index for non-encrypted files */
+#define PFT_NO_KEY 0
+
+/* PFT extended attribute name */
+#define XATTR_NAME_PFE "security.pfe"
+
+/* PFT driver requested major number */
+#define PFT_REQUESTED_MAJOR 213
+
+/* PFT driver name */
+#define DEVICE_NAME "pft"
+
+/* Maximum registered applications */
+#define PFT_MAX_APPS 1000
+
+/* Maximum command size */
+#define PFT_MAX_COMMAND_SIZE (PAGE_SIZE)
+
+/* Current Process ID */
+#define current_pid() ((u32)(current->pid))
+
+static const char *pft_state_name[PFT_STATE_MAX_INDEX] = {
+ "deactivated",
+ "deactivating",
+ "key_removed",
+ "removing_key",
+ "key_loaded",
+};
+
+/**
+ * struct pft_file_info - pft file node info.
+ * @file: pointer to file stucture.
+ * @pid: process ID.
+ * @list: next list item.
+ *
+ * A node in the list of the current open encrypted files.
+ */
+struct pft_file_info {
+ struct file *file;
+ pid_t pid;
+ struct list_head list;
+};
+
+/**
+ * struct pft_device - device state structure.
+ *
+ * @open_count: device open count.
+ * @major: device major number.
+ * @state: Per-File-Encryption state.
+ * @response: command response.
+ * @pfm_pid: PFM process id.
+ * @inplace_file: file for in-place encryption.
+ * @uid_table: registered application array (UID).
+ * @uid_count: number of registered applications.
+ * @open_file_list: open encrypted file list.
+ * @lock: lock protect list access.
+ *
+ * The open_count purpose is to ensure that only one user space
+ * application uses this driver.
+ * The open_file_list is used to close open encrypted files
+ * after the key is removed from the encryption hardware.
+ */
+struct pft_device {
+ struct cdev cdev;
+ dev_t device_no;
+ struct class *driver_class;
+ int open_count;
+ int major;
+ enum pft_state state;
+ struct pft_command_response response;
+ u32 pfm_pid;
+ struct file *inplace_file;
+ u32 *uid_table;
+ u32 uid_count;
+ struct list_head open_file_list;
+ struct mutex lock;
+};
+
+/* Device Driver State */
+static struct pft_device *pft_dev;
+
+/**
+ * pft_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static bool pft_is_ready(void)
+{
+ return (pft_dev != NULL);
+}
+
+/**
+ * file_to_filename() - get the filename from file pointer.
+ * @filp: file pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+static char *file_to_filename(struct file *filp)
+{
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
+
+ if (!filp || !filp->f_dentry)
+ return "unknown";
+
+ dentry = filp->f_dentry;
+ filename = dentry->d_iname;
+
+ return filename;
+}
+
+/**
+ * inode_to_filename() - get the filename from inode pointer.
+ * @inode: inode pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+static char *inode_to_filename(struct inode *inode)
+{
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
+
+ if (list_empty(&inode->i_dentry))
+ return "unknown";
+
+ dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+
+ filename = dentry->d_iname;
+
+ return filename;
+}
+
+/**
+ * pft_set_response() - set response error code.
+ *
+ * @error_code: The error code to return on response.
+ */
+static inline void pft_set_response(u32 error_code)
+{
+ pft_dev->response.error_code = error_code;
+}
+
+/**
+ * pft_add_file()- Add the file to the list of opened encrypted
+ * files.
+ * @filp: file to add.
+ *
+ * Return: 0 of successful operation, negative value otherwise.
+ */
+static int pft_add_file(struct file *filp)
+{
+ struct pft_file_info *node = NULL;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ pr_err("malloc failure\n");
+ return -ENOMEM;
+ }
+
+ node->file = filp;
+ INIT_LIST_HEAD(&node->list);
+
+ mutex_lock(&pft_dev->lock);
+ list_add(&node->list, &pft_dev->open_file_list);
+ pr_debug("adding file %s to open list.\n", file_to_filename(filp));
+ mutex_unlock(&pft_dev->lock);
+
+ return 0;
+}
+
+/**
+ * pft_remove_file()- Remove the given file from the list of
+ * open encrypted files.
+ * @filp: file to remove.
+ *
+ * Return: 0 on success, negative value on failure.
+ */
+static int pft_remove_file(struct file *filp)
+{
+ int ret = -ENOENT;
+ struct pft_file_info *tmp = NULL;
+ struct list_head *pos = NULL;
+ bool found = false;
+
+ mutex_lock(&pft_dev->lock);
+ list_for_each(pos, &pft_dev->open_file_list) {
+ tmp = list_entry(pos, struct pft_file_info, list);
+ if (filp == tmp->file) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ pr_debug("remove file %s. from open list.\n ",
+ file_to_filename(filp));
+ list_del(&tmp->list);
+ kfree(tmp);
+ ret = 0;
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ return ret;
+}
+
+/**
+ * pft_is_current_process_registered()- Check if current process
+ * is registered.
+ *
+ * Return: true if current process is registered.
+ */
+static bool pft_is_current_process_registered(void)
+{
+ int is_registered = false;
+ int i;
+ u32 uid = current_uid();
+
+ mutex_lock(&pft_dev->lock);
+ for (i = 0; i < pft_dev->uid_count; i++) {
+ if (pft_dev->uid_table[i] == uid) {
+ pr_debug("current UID [%u] is registerd.\n", uid);
+ is_registered = true;
+ break;
+ }
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ return is_registered;
+}
+
+/**
+ * pft_is_xattr_supported() - Check if the filesystem supports
+ * extended attributes.
+ * @indoe: pointer to the file inode
+ *
+ * Return: true if supported, false if not.
+ */
+static bool pft_is_xattr_supported(struct inode *inode)
+{
+ if (inode == NULL) {
+ pr_err("invalid argument inode passed as NULL");
+ return false;
+ }
+
+ if (inode->i_security == NULL) {
+ pr_debug("i_security is NULL, not ready yet\n");
+ return false;
+ }
+
+ if (inode->i_op == NULL) {
+ pr_debug("i_op is NULL\n");
+ return false;
+ }
+
+ if (inode->i_op->getxattr == NULL) {
+ pr_debug_once("getxattr() not supported , filename=%s\n",
+ inode_to_filename(inode));
+ return false;
+ }
+
+ if (inode->i_op->setxattr == NULL) {
+ pr_debug("setxattr() not supported\n");
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * pft_get_inode_tag() - get the file tag.
+ * @indoe: pointer to the file inode
+ *
+ * Return: tag
+ */
+static u32 pft_get_inode_tag(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ if (isec == NULL)
+ return 0;
+
+ return isec->tag;
+}
+
+/**
+ * pft_get_inode_key_index() - get the file key.
+ * @indoe: pointer to the file inode
+ *
+ * Return: key index
+ */
+static inline u32 pft_get_inode_key_index(struct inode *inode)
+{
+ return pft_get_inode_tag(inode) & PFT_TAG_KEY_MASK;
+}
+
+/**
+ * pft_is_tag_valid() - is the tag valid
+ * @indoe: pointer to the file inode
+ *
+ * The tagging is set to valid when an enterprise file is created
+ * or when an file is opened first time after power up and the
+ * xattr was checked to see if the file is encrypted or not.
+ *
+ * Return: true if the tag is valid.
+ */
+static inline bool pft_is_tag_valid(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ return ((isec->tag & PFT_TAG_MAGIC_MASK) == PFT_TAG_MAGIC) ?
+ true : false;
+}
+
+/**
+ * pft_is_file_encrypted() - is inode tagged as encrypted.
+ *
+ * @tag: holds the key index and tagging flags.
+ *
+ * Return: true if the file is encrypted.
+ */
+static inline bool pft_is_file_encrypted(u32 tag)
+{
+ return (tag & PFT_TAG_ENCRYPTED) ? true : false;
+}
+
+/**
+ * pft_tag_inode_non_encrypted() - Tag the inode as
+ * non-encrypted.
+ * @indoe: pointer to the file inode
+ *
+ * Tag file as non-encrypted, only the valid bit is set,
+ * the encrypted bit is not set.
+ */
+static inline void pft_tag_inode_non_encrypted(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->tag = (u32)(PFT_TAG_MAGIC);
+}
+
+/**
+ * pft_tag_inode_encrypted() - Tag the inode as encrypted.
+ * @indoe: pointer to the file inode
+ *
+ * Set the valid bit, the encrypted bit, and the key index.
+ */
+static void pft_tag_inode_encrypted(struct inode *inode, u32 key_index)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->tag = key_index | PFT_TAG_ENCRYPTED | PFT_TAG_MAGIC;
+}
+
+/**
+ * pft_get_file_tag()- get the file tag.
+ * @dentry: pointer to file dentry.
+ * @tag_ptr: pointer to tag.
+ *
+ * This is the major function for detecting tag files.
+ * Get the tag from the inode if tag is valid,
+ * or from the xattr if this is the 1st time after power up.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+static int pft_get_file_tag(struct dentry *dentry, u32 *tag_ptr)
+{
+ ssize_t size = 0;
+ struct inode *inode;
+ const char *xattr_name = XATTR_NAME_PFE;
+ u32 key;
+
+ if (!dentry || !dentry->d_inode || !tag_ptr) {
+ pr_err("invalid param");
+ return -EINVAL;
+ }
+
+ inode = dentry->d_inode;
+ if (pft_is_tag_valid(inode)) {
+ *tag_ptr = pft_get_inode_tag(inode);
+ return 0;
+ }
+
+ /*
+ * For the first time reading the tag, the tag is not valid, hence
+ * get xattr.
+ */
+ size = inode->i_op->getxattr(dentry, xattr_name, &key, sizeof(key));
+
+ if (size == -ENODATA || size == -EOPNOTSUPP) {
+ pft_tag_inode_non_encrypted(inode);
+ *tag_ptr = pft_get_inode_tag(inode);
+ } else if (size > 0) {
+ pr_debug("First time file %s opened, found xattr = %u.\n",
+ inode_to_filename(inode), key);
+ pft_tag_inode_encrypted(inode, key);
+ *tag_ptr = pft_get_inode_tag(inode);
+ } else {
+ pr_err("getxattr() failure, ret=%d.\n", size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pft_tag_file() - Tag the file saving the key_index.
+ * @dentry: file dentry.
+ * @key_index: encryption key index.
+ *
+ * This is the major fuction for tagging a file.
+ * Tag the file on both the xattr and the inode.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+static int pft_tag_file(struct dentry *dentry, u32 key_index)
+{
+ int size = 0;
+ const char *xattr_name = XATTR_NAME_PFE;
+
+ if (!dentry || !dentry->d_inode) {
+ pr_err("invalid NULL param");
+ return -EINVAL;
+ }
+
+ if (!pft_is_xattr_supported(dentry->d_inode)) {
+ pr_err("set xattr for file %s is not support.\n",
+ dentry->d_iname);
+ return -EINVAL;
+ }
+
+ size = dentry->d_inode->i_op->setxattr(dentry, xattr_name, &key_index,
+ sizeof(key_index), 0);
+ if (size < 0) {
+ pr_err("failed to set xattr for file %s, ret =%d.\n",
+ dentry->d_iname, size);
+ return -EFAULT;
+ }
+
+ pft_tag_inode_encrypted(dentry->d_inode, key_index);
+ pr_debug("file %s tagged encrypted\n", dentry->d_iname);
+
+ return 0;
+}
+
+/**
+ * pft_get_app_key_index() - get the application key index.
+ * @uid: registered application UID
+ *
+ * Get key index based on the given registered application UID.
+ * Currently only one key is supported.
+ *
+ * Return: encryption key index.
+ */
+static inline u32 pft_get_app_key_index(u32 uid)
+{
+ return PFT_DEFAULT_KEY_INDEX;
+}
+
+/**
+ * pft_is_encrypted_file() - is the file encrypted.
+ * @dentry: file pointer.
+ *
+ * Return: true if the file is encrypted, false otherwise.
+ */
+static bool pft_is_encrypted_file(struct dentry *dentry)
+{
+ int rc;
+ u32 tag;
+
+ if (!pft_is_ready())
+ return false;
+
+ if (!pft_is_xattr_supported(dentry->d_inode))
+ return false;
+
+ rc = pft_get_file_tag(dentry, &tag);
+ if (rc < 0)
+ return false;
+
+ return pft_is_file_encrypted(tag);
+}
+
+/**
+ * pft_is_encrypted_inode() - is the file encrypted.
+ * @inode: inode of file to check.
+ *
+ * Return: true if the file is encrypted, false otherwise.
+ */
+static bool pft_is_encrypted_inode(struct inode *inode)
+{
+ u32 tag;
+
+ if (!pft_is_ready())
+ return false;
+
+ if (!pft_is_xattr_supported(inode))
+ return false;
+
+ tag = pft_get_inode_tag(inode);
+
+ return pft_is_file_encrypted(tag);
+}
+
+/**
+ * pft_is_inplace_inode() - is this the inode of file for
+ * in-place encryption.
+ * @inode: inode of file to check.
+ *
+ * Return: true if this file is being encrypted, false
+ * otherwise.
+ */
+static bool pft_is_inplace_inode(struct inode *inode)
+{
+ if (!pft_dev->inplace_file || !pft_dev->inplace_file->f_path.dentry)
+ return false;
+
+ return (pft_dev->inplace_file->f_path.dentry->d_inode == inode);
+}
+
+/**
+ * pft_is_inplace_file() - is this the file for in-place
+ * encryption.
+ * @filp: file to check.
+ *
+ * A file struct might be allocated per process, inode should be
+ * only one.
+ *
+ * Return: true if this file is being encrypted, false
+ * otherwise.
+ */
+static inline bool pft_is_inplace_file(struct file *filp)
+{
+ if (!filp || !filp->f_path.dentry || !filp->f_path.dentry->d_inode)
+ return false;
+
+ return pft_is_inplace_inode(filp->f_path.dentry->d_inode);
+}
+
+/**
+ * pft_get_key_index() - get the key index and other indications
+ * @inode: Pointer to inode struct
+ * @key_index: Pointer to the return value of key index
+ * @is_encrypted: Pointer to the return value.
+ * @is_inplace: Pointer to the return value.
+ *
+ * Provides the given inode's encryption key index, and well as
+ * indications whether the file is encrypted or is it currently
+ * being in-placed encrypted.
+ * This API is called by the dm-req-crypt to decide if to
+ * encrypt/decrypt the file.
+ * File tagging depends on the hooks to be called from selinux,
+ * so if selinux is disabled then tagging is also not
+ * valid.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_get_key_index(struct inode *inode, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace)
+{
+ u32 tag = 0;
+
+ if (!pft_is_ready())
+ return -ENODEV;
+
+ if (!selinux_is_enabled())
+ return -ENODEV;
+
+ if (!inode)
+ return -EPERM;
+
+ if (!is_encrypted) {
+ pr_err("is_encrypted is NULL\n");
+ return -EPERM;
+ }
+ if (!is_inplace) {
+ pr_err("is_inplace is NULL\n");
+ return -EPERM;
+ }
+ if (!key_index) {
+ pr_err("key_index is NULL\n");
+ return -EPERM;
+ }
+
+ if (!pft_is_tag_valid(inode)) {
+ pr_debug("file %s, Tag not valid\n", inode_to_filename(inode));
+ return -EINVAL;
+ }
+
+ if (!pft_is_xattr_supported(inode)) {
+ *is_encrypted = false;
+ *is_inplace = false;
+ *key_index = 0;
+ return 0;
+ }
+
+ tag = pft_get_inode_tag(inode);
+
+ *is_encrypted = pft_is_file_encrypted(tag);
+ *key_index = pft_get_inode_key_index(inode);
+ *is_inplace = pft_is_inplace_inode(inode);
+
+ if (*is_encrypted)
+ pr_debug("file %s is encrypted\n", inode_to_filename(inode));
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_get_key_index);
+
+/**
+ * pft_bio_get_inode() - get the inode from a bio.
+ * @bio: Pointer to BIO structure.
+ *
+ * Walk the bio struct links to get the inode.
+ *
+ * Return: pointer to the inode struct if successful, or NULL otherwise.
+ */
+static struct inode *pft_bio_get_inode(struct bio *bio)
+{
+ if (!bio || !bio->bi_io_vec || !bio->bi_io_vec->bv_page ||
+ !bio->bi_io_vec->bv_page->mapping)
+ return NULL;
+
+ return bio->bi_io_vec->bv_page->mapping->host;
+}
+
+/**
+ * pft_allow_merge_bio()- Check if 2 BIOs can be merged.
+ * @bio1: Pointer to first BIO structure.
+ * @bio2: Pointer to second BIO structure.
+ *
+ * Prevent merging of BIOs from encrypted and non-encrypted
+ * files, or files encrypted with different key.
+ * This API is called by the file system block layer.
+ *
+ * Return: true if the BIOs allowed to be merged, false
+ * otherwise.
+ */
+bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{
+ u32 key_index1 = 0, key_index2 = 0;
+ bool is_encrypted1 = false, is_encrypted2 = false;
+ bool allow = false;
+ bool is_inplace = false; /* N.A. */
+ int ret;
+
+ if (!pft_is_ready())
+ return true;
+
+ ret = pft_get_key_index(pft_bio_get_inode(bio1), &key_index1,
+ &is_encrypted1, &is_inplace);
+ if (ret)
+ is_encrypted1 = false;
+
+ ret = pft_get_key_index(pft_bio_get_inode(bio2), &key_index2,
+ &is_encrypted2, &is_inplace);
+ if (ret)
+ is_encrypted2 = false;
+
+ allow = ((is_encrypted1 == is_encrypted2) &&
+ (key_index1 == key_index2));
+
+ return allow;
+}
+EXPORT_SYMBOL(pft_allow_merge_bio);
+
+/**
+ * pft_inode_create() - file creation callback.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ *
+ * This hook is called when file is created by VFS.
+ * This hook is called from the selinux driver.
+ * This hooks check file creation permission for enterprise
+ * applications.
+ * Call path:
+ * vfs_create()->security_inode_create()->selinux_inode_create()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ if (!dir || !dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_LOADED:
+ break;
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* At this state no new encrypted files can be created */
+ if (pft_is_current_process_registered()) {
+ pr_debug("key removed, registered uid %u is denied from creating new file %s\n",
+ current_uid(), dentry->d_iname);
+ return -EACCES;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL(pft_inode_create);
+
+/**
+ * pft_inode_post_create() - file creation callback.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ *
+ * This hook is called when file is created by VFS.
+ * This hook is called from the selinux driver.
+ * This hooks tags new files as encrypted when created by
+ * enterprise applications.
+ * Call path:
+ * vfs_create()->security_inode_post_create()->selinux_inode_post_create()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ int ret;
+
+ if (!dir || !dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ break;
+ case PFT_STATE_KEY_LOADED:
+ /* Check whether the new file should be encrypted */
+ if (pft_is_current_process_registered()) {
+ u32 key_index = pft_get_app_key_index(current_uid());
+ ret = pft_tag_file(dentry, key_index);
+ if (ret == 0)
+ pr_debug("key loaded, pid [%u] uid [%d] is creating file %s\n",
+ current_pid(), current_uid(),
+ dentry->d_iname);
+ else {
+ pr_err("Failed to tag file %s by pid %d\n",
+ dentry->d_iname, current_pid());
+ return -EFAULT;
+ }
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_post_create);
+
+/**
+ * pft_inode_mknod() - mknode file hook (callback)
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ * @dev:
+ *
+ * This hook checks encrypted file access permission by
+ * enterprise application.
+ * Call path:
+ * vfs_mknod()->security_inode_mknod()->selinux_inode_mknod()->pft_inode_mknod()
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{
+ int rc;
+
+ /* Check if allowed to create new encrypted files */
+ rc = pft_inode_create(dir, dentry, mode);
+
+ return rc;
+}
+EXPORT_SYMBOL(pft_inode_mknod);
+
+/**
+ * pft_inode_symlink() - symlink file hook (callback)
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @name: Old file name
+ *
+ * Allow only enterprise app to create symlink to enterprise
+ * file.
+ * Call path:
+ * vfs_symlink()->security_inode_symlink()->selinux_inode_symlink()
+ *
+ * Return: 0 on allowed operation, negative value otherwise.
+ */
+int pft_inode_symlink(struct inode *dir, struct dentry *dentry,
+ const char *name)
+{
+ struct inode *inode;
+
+ if (!dir) {
+ pr_err("dir is NULL.\n");
+ return 0;
+ }
+ if (!dentry) {
+ pr_err("dentry is NULL.\n");
+ return 0;
+ }
+ if (!name) {
+ pr_err("name is NULL.\n");
+ return 0;
+ }
+
+ pr_debug("symlink for file [%s] dir [%s] dentry [%s] started! ....\n",
+ name, inode_to_filename(dir), dentry->d_iname);
+ inode = dentry->d_inode;
+
+ if (!dentry->d_inode) {
+ pr_debug("d_inode is NULL.\n");
+ return 0;
+ }
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_inode(inode))
+ return 0;
+
+ /*
+ * Only PFM allowed to access in-place-encryption-file
+ * during in-place-encryption process
+ */
+ if (pft_is_inplace_inode(inode)) {
+ pr_err("symlink for in-place-encryption file %s by pid %d is blocked.\n",
+ inode_to_filename(inode), current_pid());
+ return -EACCES;
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* Block any access for encrypted files when key not loaded */
+ pr_debug("key not loaded. uid (%u) can not access file %s\n",
+ current_uid(), inode_to_filename(inode));
+ return -EACCES;
+ case PFT_STATE_KEY_LOADED:
+ /* Only registered apps may access encrypted files. */
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app uid %u pid %u is trying to access encrypted file %s\n",
+ current_uid(), current_pid(), name);
+ return -EACCES;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ pr_debug("symlink for file %s ok.\n", name);
+
+ return 0;
+
+}
+EXPORT_SYMBOL(pft_inode_symlink);
+
+/**
+ * pft_inode_rename() - file rename hook.
+ * @inode: directory inode
+ * @dentry: file dentry
+ * @new_inode
+ * @new_dentry
+ *
+ * Block attempt to rename enterprise file.
+ *
+ * Return: 0 on allowed operation, negative value otherwise.
+ */
+int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ if (!inode || !dentry || !new_inode || !new_dentry || !dentry->d_inode)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(dentry))
+ return 0;
+
+ pr_debug("attempt to rename encrypted file [%s]\n", dentry->d_iname);
+
+ if (pft_is_inplace_inode(dentry->d_inode)) {
+ pr_err("access in-place-encryption file %s by uid [%d] pid [%d] is blocked.\n",
+ inode_to_filename(inode), current_uid(), current_pid());
+ return -EACCES;
+ }
+
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ current_uid(), current_pid(), dentry->d_iname);
+ return -EACCES;
+ } else
+ pr_debug("rename file %s\n", dentry->d_iname);
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_rename);
+
+/**
+ * pft_file_open() - file open hook (callback).
+ * @filp: file pointer
+ * @cred: credentials pointer
+ *
+ * This hook is called when file is opened by VFS.
+ * It is called from the selinux driver.
+ * It checks enterprise file xattr when first opened.
+ * It adds encrypted file to the list of open files.
+ * Call path:
+ * do_filp_open()->security_dentry_open()->selinux_dentry_open()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_file_open(struct file *filp, const struct cred *cred)
+{
+ if (!filp || !filp->f_path.dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_dentry))
+ return 0;
+
+ /*
+ * Only PFM allowed to access in-place-encryption-file
+ * during in-place-encryption process
+ */
+ if (pft_is_inplace_file(filp) && current_pid() != pft_dev->pfm_pid) {
+ pr_err("Access in-place-encryption file %s by uid %d pid %d is blocked.\n",
+ file_to_filename(filp), current_uid(), current_pid());
+ return -EACCES;
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* Block any access for encrypted files when key not loaded */
+ pr_debug("key not loaded. uid (%u) can not access file %s\n",
+ current_uid(), file_to_filename(filp));
+ return -EACCES;
+ case PFT_STATE_KEY_LOADED:
+ /* Only registered apps may access encrypted files. */
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ current_uid(), current_pid(),
+ file_to_filename(filp));
+ return -EACCES;
+ }
+
+ pft_add_file(filp);
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_open);
+
+/**
+ * pft_file_permission() - check file access permission.
+ * @filp: file pointer
+ * @mask: flags
+ *
+ * This hook is called when file is read/write by VFS.
+ * This hook is called from the selinux driver.
+ * This hook checks encrypted file access permission by
+ * enterprise application.
+ * Call path:
+ * vfs_read()->security_file_permission()->selinux_file_permission()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_file_permission(struct file *filp, int mask)
+{
+ if (!filp)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_dentry))
+ return 0;
+
+ /*
+ * Only PFM allowed to access in-place-encryption-file
+ * during in-place encryption process
+ */
+ if (pft_is_inplace_file(filp)) {
+ if (current_pid() == pft_dev->pfm_pid) {
+ /* mask MAY_WRITE=2 / MAY_READ=4 */
+ pr_debug("r/w [mask 0x%x] in-place-encryption file %s by PFM (UID %d, PID %d).\n",
+ mask, file_to_filename(filp),
+ current_uid(), current_pid());
+ return 0;
+ } else {
+ pr_err("Access in-place-encryption file %s by App (UID %d, PID %d) is blocked.\n",
+ file_to_filename(filp),
+ current_uid(), current_pid());
+ return -EACCES;
+ }
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* Block any access for encrypted files when key not loaded */
+ pr_debug("key not loaded. uid (%u) can not access file %s\n",
+ current_uid(), file_to_filename(filp));
+ return -EACCES;
+ case PFT_STATE_KEY_LOADED:
+ /* Only registered apps can access encrypted files. */
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ current_uid(), current_pid(),
+ file_to_filename(filp));
+ return -EACCES;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_permission);
+
+/**
+ * pft_sync_file() - sync the file.
+ * @filp: file pointer
+ *
+ * Complete writting any pending write request of encrypted data
+ * before key is removed, to avoid writting garbage to
+ * enterprise files.
+ */
+static void pft_sync_file(struct file *filp)
+{
+ int ret;
+
+ ret = vfs_fsync(filp, false);
+
+ if (ret)
+ pr_debug("failed to sync file %s, ret = %d.\n",
+ file_to_filename(filp), ret);
+ else
+ pr_debug("Sync file %s ok.\n", file_to_filename(filp));
+
+}
+
+/**
+ * pft_file_close()- handle file close event
+ * @filp: file pointer
+ *
+ * This hook is called when file is closed by VFS.
+ * This hook is called from the selinux driver.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_file_close(struct file *filp)
+{
+ if (!filp)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_dentry))
+ return 0;
+
+ if (pft_is_inplace_file(filp)) {
+ pr_debug("pid [%u] uid [%u] is closing in-place-encryption file %s\n",
+ current_pid(), current_uid(), file_to_filename(filp));
+ pft_dev->inplace_file = NULL;
+ }
+
+ pft_sync_file(filp);
+ pft_remove_file(filp);
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_close);
+
+/**
+ * pft_inode_unlink() - Delete file hook.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ *
+ * call path: vfs_unlink()->security_inode_unlink().
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+
+ if (!dir || !dentry || !dentry->d_inode)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ inode = dentry->d_inode;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(dentry))
+ return 0;
+
+ if (pft_is_inplace_inode(inode)) {
+ pr_err("block delete in-place-encryption file %s by uid [%d] pid [%d], while encryption in progress.\n",
+ inode_to_filename(inode), current_uid(), current_pid());
+ return -EACCES;
+ }
+
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ current_uid(), current_pid(), inode_to_filename(inode));
+ return -EACCES;
+ } else
+ pr_debug("delete file %s\n", inode_to_filename(inode));
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_unlink);
+
+/**
+ * pft_inode_set_xattr() - set/remove xattr callback.
+ * @dentry: file dentry pointer
+ * @name: xattr name.
+ *
+ * This hook checks attempt to set/remove PFE xattr.
+ * Only this kernel driver allows to set the PFE xattr, so block
+ * any attempt to do it from user space. Allow access for other
+ * xattr.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_inode_set_xattr(struct dentry *dentry, const char *name)
+{
+ struct inode *inode = NULL;
+
+ if (!dentry || !dentry->d_inode)
+ return 0;
+
+ inode = dentry->d_inode;
+
+ if (strcmp(name, XATTR_NAME_PFE) != 0) {
+ pr_debug("xattr name=%s file %s\n", name,
+ inode_to_filename(inode));
+ return 0; /* Not PFE xattr so it is ok */
+ }
+
+ pr_err("Attemp to set/remove PFE xattr for file %s\n",
+ inode_to_filename(inode));
+
+ /* Only PFT kernel driver allows to set the PFE xattr */
+ return -EACCES;
+}
+EXPORT_SYMBOL(pft_inode_set_xattr);
+
+/**
+ * pft_close_opened_enc_files() - Close all the currently open
+ * encrypted files
+ *
+ * Close all open encrypted file when removing key or
+ * deactivating.
+ */
+static void pft_close_opened_enc_files(void)
+{
+ struct pft_file_info *tmp = NULL;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+
+ list_for_each_safe(pos, next, &pft_dev->open_file_list) {
+ struct file *filp;
+ tmp = list_entry(pos, struct pft_file_info, list);
+ filp = tmp->file;
+ pr_debug("closing file %s.\n", file_to_filename(filp));
+ /* filp_close() eventually calls pft_file_close() */
+ filp_close(filp, NULL);
+ }
+}
+
+/**
+ * pft_set_state() - Handle "Set State" command
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_set_state(struct pft_command *command, int size)
+{
+ u32 state = command->set_state.state;
+ int expected_size = sizeof(command->opcode) +
+ sizeof(command->set_state);
+
+ if (size != expected_size) {
+ pr_err("Invalid buffer size\n");
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ if (state >= PFT_STATE_MAX_INDEX) {
+ pr_err("Invalid state %d\n", command->set_state.state);
+ pft_set_response(PFT_CMD_RESP_INVALID_STATE);
+ return 0;
+ }
+
+ pr_debug("Set State %d [%s].\n", state, pft_state_name[state]);
+
+ switch (command->set_state.state) {
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ pft_close_opened_enc_files();
+ /* Fall through */
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_LOADED:
+ case PFT_STATE_KEY_REMOVED:
+ pft_dev->state = command->set_state.state;
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ break;
+ default:
+ pr_err("Invalid state %d\n", command->set_state.state);
+ pft_set_response(PFT_CMD_RESP_INVALID_STATE);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * pft_get_process_open_file() - get file pointer using file
+ * descriptor index.
+ * @index: file descriptor index.
+ *
+ * Return: file pointer on success, NULL on failure.
+ */
+static struct file *pft_get_process_open_file(int index)
+{
+ struct fdtable *files_table;
+
+ files_table = files_fdtable(current->files);
+ if (files_table == NULL)
+ return NULL;
+
+ if (index >= files_table->max_fds)
+ return NULL;
+ else
+ return files_table->fd[index];
+}
+
+/**
+ * pft_set_inplace_file() - handle "inplace file encryption"
+ * command.
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 if command is valid, negative value otherwise.
+ */
+static int pft_set_inplace_file(struct pft_command *command, int size)
+{
+ int expected_size;
+ u32 fd;
+ int rc;
+ struct file *filp = NULL;
+ struct inode *inode = NULL;
+ int writecount;
+
+ expected_size = sizeof(command->opcode) +
+ sizeof(command->preform_in_place_file_enc.file_descriptor);
+
+ if (size != expected_size) {
+ pr_err("invalid command size %d expected %d.\n",
+ size, expected_size);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ if (pft_dev->state != (u32) PFT_STATE_KEY_LOADED) {
+ pr_err("Key not loaded, state [%d], In-place-encryption is not allowed.\n",
+ pft_dev->state);
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ /* allow only one in-place file encryption at a time */
+ if (pft_dev->inplace_file != NULL) {
+ pr_err("file %s in-place-encryption in progress.\n",
+ file_to_filename(pft_dev->inplace_file));
+ /* @todo - use new error code */
+ pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
+ return 0;
+ }
+
+ fd = command->preform_in_place_file_enc.file_descriptor;
+ filp = pft_get_process_open_file(fd);
+
+ if (filp == NULL) {
+ pr_err("failed to find file by fd %d.\n", fd);
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ /* Verify the file is not already open by other than PFM */
+ if (!filp->f_path.dentry || !filp->f_path.dentry->d_inode) {
+ pr_err("failed to get inode of inplace-file.\n");
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ inode = filp->f_path.dentry->d_inode;
+ writecount = atomic_read(&inode->i_writecount);
+ if (writecount > 1) {
+ pr_err("file %s is opened %d times for write.\n",
+ file_to_filename(filp), writecount);
+ pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
+ return 0;
+ }
+
+ /*
+ * Check if the file was already encryprted.
+ * In practice, it is unlikely to happen,
+ * because PFM is not an enterprise application
+ * it won't be able to open encrypted file.
+ */
+ if (pft_is_encrypted_file(filp->f_dentry)) {
+ pr_err("file %s is already encrypted.\n",
+ file_to_filename(filp));
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+
+ /* Update the current in-place-encryption file */
+ pft_dev->inplace_file = filp;
+
+ /*
+ * Now, any new access to this file is allowed only to PFM.
+ * Lets make sure that all pending writes are completed
+ * before encrypting the file.
+ */
+ pft_sync_file(filp);
+
+ rc = pft_tag_file(pft_dev->inplace_file->f_dentry,
+ pft_get_app_key_index(current_uid()));
+
+ if (!rc) {
+ pr_debug("tagged file %s to be encrypted.\n",
+ file_to_filename(pft_dev->inplace_file));
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ } else {
+ pr_err("failed to tag file %s for encryption.\n",
+ file_to_filename(pft_dev->inplace_file));
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ }
+
+ return 0;
+}
+
+/**
+ * pft_update_reg_apps() - Update the registered application
+ * list.
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_update_reg_apps(struct pft_command *command, int size)
+{
+ int i;
+ int expected_size;
+ void *buf;
+ int buf_size;
+ u32 items_count = command->update_app_list.items_count;
+
+ if (items_count > PFT_MAX_APPS) {
+ pr_err("Number of apps [%d] > max apps [%d]\n",
+ items_count , PFT_MAX_APPS);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ expected_size =
+ sizeof(command->opcode) +
+ sizeof(command->update_app_list.items_count) +
+ (command->update_app_list.items_count * sizeof(u32));
+
+ if (size != expected_size) {
+ pr_err("invalid command size %d expected %d.\n",
+ size, expected_size);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pft_dev->lock);
+
+ /* Free old table */
+ kfree(pft_dev->uid_table);
+ pft_dev->uid_table = NULL;
+ pft_dev->uid_count = 0;
+
+ if (items_count == 0) {
+ pr_info("empty app list - clear list.\n");
+ mutex_unlock(&pft_dev->lock);
+ return 0;
+ }
+
+ buf_size = command->update_app_list.items_count * sizeof(u32);
+ buf = kzalloc(buf_size, GFP_KERNEL);
+
+ if (!buf) {
+ pr_err("malloc failure\n");
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ mutex_unlock(&pft_dev->lock);
+ return 0;
+ }
+
+ pft_dev->uid_table = buf;
+ pft_dev->uid_count = command->update_app_list.items_count;
+ pr_debug("uid_count = %d\n", pft_dev->uid_count);
+ for (i = 0; i < pft_dev->uid_count; i++)
+ pft_dev->uid_table[i] = command->update_app_list.table[i];
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ mutex_unlock(&pft_dev->lock);
+
+ return 0;
+}
+
+/**
+ * pft_handle_command() - Handle user space app commands.
+ * @buf: command buffer.
+ * @buf_size: command buffer size.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_handle_command(void *buf, int buf_size)
+{
+ size_t ret = 0;
+ struct pft_command *command = NULL;
+
+ /* opcode field is the minimum length of command */
+ if (buf_size < sizeof(command->opcode)) {
+ pr_err("Invalid argument used buffer size\n");
+ return -EINVAL;
+ }
+
+ command = (struct pft_command *)buf;
+
+ pft_dev->response.command_id = command->opcode;
+
+ switch (command->opcode) {
+ case PFT_CMD_OPCODE_SET_STATE:
+ ret = pft_set_state(command, buf_size);
+ break;
+ case PFT_CMD_OPCODE_UPDATE_REG_APP_UID:
+ ret = pft_update_reg_apps(command, buf_size);
+ break;
+ case PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC:
+ ret = pft_set_inplace_file(command, buf_size);
+ break;
+ default:
+ pr_err("Invalid command_op_code %u\n", command->opcode);
+ pft_set_response(PFT_CMD_RESP_INVALID_COMMAND);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int pft_device_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ mutex_lock(&pft_dev->lock);
+ if (pft_dev->open_count > 0) {
+ pr_err("PFT device is already opened (%d)\n",
+ pft_dev->open_count);
+ ret = -EBUSY;
+ } else {
+ pft_dev->open_count++;
+ pft_dev->pfm_pid = current_pid();
+ pr_debug("PFT device opened by %d (%d)\n",
+ pft_dev->pfm_pid, pft_dev->open_count);
+ ret = 0;
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ pr_debug("device opened, count %d\n", pft_dev->open_count);
+
+ return ret;
+}
+
+static int pft_device_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&pft_dev->lock);
+ if (0 < pft_dev->open_count)
+ pft_dev->open_count--;
+ pft_dev->pfm_pid = UINT_MAX;
+ mutex_unlock(&pft_dev->lock);
+
+ pr_debug("device released, count %d\n", pft_dev->open_count);
+
+ return 0;
+}
+
+/**
+ * pft_device_write() - Get commands from user sapce.
+ *
+ * Return: number of bytes to write on success to get the
+ * command buffer, negative value on failure.
+ * The error code for handling the command should be retrive by
+ * reading the response.
+ * Note: any reurn value of 0..size-1 will cause retry by the
+ * OS, so avoid it.
+ */
+static ssize_t pft_device_write(struct file *filp, const char __user *user_buff,
+ size_t size, loff_t *f_pos)
+{
+ int ret;
+ char *cmd_buf;
+
+ if (size > PFT_MAX_COMMAND_SIZE || !user_buff || !f_pos) {
+ pr_err("inavlid parameters.\n");
+ return -EINVAL;
+ }
+
+ cmd_buf = kzalloc(size, GFP_KERNEL);
+ if (cmd_buf == NULL) {
+ pr_err("malloc failure for command buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = copy_from_user(cmd_buf, user_buff, size);
+ if (ret) {
+ pr_err("Unable to copy from user (err %d)\n", ret);
+ kfree(cmd_buf);
+ return -EFAULT;
+ }
+
+ ret = pft_handle_command(cmd_buf, size);
+ if (ret) {
+ kfree(cmd_buf);
+ return -EFAULT;
+ }
+
+ kfree(cmd_buf);
+
+ return size;
+}
+
+/**
+ * pft_device_read() - return response of last command.
+ *
+ * Return: number of bytes to read on success, negative value on
+ * failure.
+ */
+static ssize_t pft_device_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *f_pos)
+{
+ int ret = 0;
+
+ if (!buffer || !f_pos || length < sizeof(pft_dev->response)) {
+ pr_err("inavlid parameters.\n");
+ return -EFAULT;
+ }
+
+ ret = copy_to_user(buffer, &(pft_dev->response),
+ sizeof(pft_dev->response));
+ if (ret) {
+ pr_err("Unable to copy to user, err = %d.\n", ret);
+ return -EINVAL;
+ }
+
+ return sizeof(pft_dev->response);
+}
+
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .read = pft_device_read,
+ .write = pft_device_write,
+ .open = pft_device_open,
+ .release = pft_device_release,
+};
+
+static int __init pft_register_chardev(void)
+{
+ int rc;
+ unsigned baseminor = 0;
+ unsigned count = 1;
+ struct device *class_dev;
+
+ rc = alloc_chrdev_region(&pft_dev->device_no, baseminor, count,
+ DEVICE_NAME);
+ if (rc < 0) {
+ pr_err("alloc_chrdev_region failed %d\n", rc);
+ return rc;
+ }
+
+ pft_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(pft_dev->driver_class)) {
+ rc = -ENOMEM;
+ pr_err("class_create failed %d\n", rc);
+ goto exit_unreg_chrdev_region;
+ }
+
+ class_dev = device_create(pft_dev->driver_class, NULL,
+ pft_dev->device_no, NULL,
+ DEVICE_NAME);
+ if (!class_dev) {
+ pr_err("class_device_create failed %d\n", rc);
+ rc = -ENOMEM;
+ goto exit_destroy_class;
+ }
+
+ cdev_init(&pft_dev->cdev, &fops);
+ pft_dev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&pft_dev->cdev, MKDEV(MAJOR(pft_dev->device_no), 0), 1);
+ if (rc < 0) {
+ pr_err("cdev_add failed %d\n", rc);
+ goto exit_destroy_device;
+ }
+
+ return 0;
+
+exit_destroy_device:
+ device_destroy(pft_dev->driver_class, pft_dev->device_no);
+exit_destroy_class:
+ class_destroy(pft_dev->driver_class);
+exit_unreg_chrdev_region:
+ unregister_chrdev_region(pft_dev->device_no, 1);
+ return rc;
+}
+
+static void __exit pft_unregister_chrdev(void)
+{
+ cdev_del(&pft_dev->cdev);
+ device_destroy(pft_dev->driver_class, pft_dev->device_no);
+ class_destroy(pft_dev->driver_class);
+ unregister_chrdev_region(pft_dev->device_no, 1);
+
+}
+
+static void __exit pft_exit(void)
+{
+ if (pft_dev == NULL)
+ return;
+
+ pft_unregister_chrdev();
+
+ kfree(pft_dev->uid_table);
+ kfree(pft_dev);
+}
+
+static int __init pft_init(void)
+{
+ int ret;
+ struct pft_device *dev = NULL;
+
+ dev = kzalloc(sizeof(struct pft_device), GFP_KERNEL);
+ if (dev == NULL) {
+ pr_err("No memory for device structr\n");
+ return -ENOMEM;
+ }
+
+ dev->state = PFT_STATE_DEACTIVATED;
+ INIT_LIST_HEAD(&dev->open_file_list);
+ mutex_init(&dev->lock);
+
+ pft_dev = dev;
+
+ ret = pft_register_chardev();
+ if (ret) {
+ pr_err("create character device failed.\n");
+ goto fail;
+ }
+
+ pr_info("Drivr initialized successfully %s %s.n", __DATE__, __TIME__);
+
+ return 0;
+
+fail:
+ pr_err("Failed to init driver.\n");
+ kfree(dev);
+
+ return -ENODEV;
+}
+
+module_init(pft_init);
+module_exit(pft_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Tagger driver");
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index e373b9b..e3d339c 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -552,7 +552,7 @@
dotg->charger->chg_type == DWC3_PROPRIETARY_CHARGER)
power_supply_type = POWER_SUPPLY_TYPE_USB_DCP;
else
- power_supply_type = POWER_SUPPLY_TYPE_BATTERY;
+ power_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
power_supply_set_supply_type(dotg->psy, power_supply_type);
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index ebb226c..f319238 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -82,6 +82,7 @@
bool wakeup_irq_enabled;
int wakeup_irq;
enum usb_vdd_type vdd_type;
+ void __iomem *usb_phy_ctrl_reg;
};
static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
@@ -497,6 +498,9 @@
if (time_after(jiffies, timeout)) {
dev_err(mhcd->dev, "msm_ulpi_read: timeout %08x\n",
readl_relaxed(USB_ULPI_VIEWPORT));
+ dev_err(mhcd->dev, "PORTSC: %08x USBCMD: %08x\n",
+ readl_relaxed(USB_PORTSC),
+ readl_relaxed(USB_USBCMD));
return -ETIMEDOUT;
}
udelay(1);
@@ -521,6 +525,9 @@
while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
if (time_after(jiffies, timeout)) {
dev_err(mhcd->dev, "msm_ulpi_write: timeout\n");
+ dev_err(mhcd->dev, "PORTSC: %08x USBCMD: %08x\n",
+ readl_relaxed(USB_PORTSC),
+ readl_relaxed(USB_USBCMD));
return -ETIMEDOUT;
}
udelay(1);
@@ -572,13 +579,13 @@
struct msm_usb_host_platform_data *pdata;
u32 val;
int ret;
- int retries;
ret = msm_ehci_link_clk_reset(mhcd, 1);
if (ret)
return ret;
- usleep_range(10, 12);
+ /* Minimum 10msec delay for block reset as per hardware spec */
+ usleep_range(10000, 12000);
ret = msm_ehci_link_clk_reset(mhcd, 0);
if (ret)
@@ -592,29 +599,34 @@
val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
- for (retries = 3; retries > 0; retries--) {
- ret = msm_ulpi_write(mhcd, ULPI_FUNC_CTRL_SUSPENDM,
- ULPI_CLR(ULPI_FUNC_CTRL));
- if (!ret)
- break;
- }
- if (!retries)
- return -ETIMEDOUT;
-
- /* Wakeup the PHY with a reg-access for calibration */
- for (retries = 3; retries > 0; retries--) {
- ret = msm_ulpi_read(mhcd, ULPI_DEBUG);
- if (ret != -ETIMEDOUT)
- break;
- }
- if (!retries)
- return -ETIMEDOUT;
-
dev_info(mhcd->dev, "phy_reset: success\n");
return 0;
}
+static void usb_phy_reset(struct msm_hcd *mhcd)
+{
+ u32 val;
+
+ /* Assert USB PHY_PON */
+ val = readl_relaxed(mhcd->usb_phy_ctrl_reg);
+ val &= ~PHY_POR_BIT_MASK;
+ val |= PHY_POR_ASSERT;
+ writel_relaxed(val, mhcd->usb_phy_ctrl_reg);
+
+ /* wait for minimum 10 microseconds as suggested in hardware spec */
+ usleep_range(10, 15);
+
+ /* Deassert USB PHY_PON */
+ val = readl_relaxed(mhcd->usb_phy_ctrl_reg);
+ val &= ~PHY_POR_BIT_MASK;
+ val |= PHY_POR_DEASSERT;
+ writel_relaxed(val, mhcd->usb_phy_ctrl_reg);
+
+ /* Ensure that RESET operation is completed. */
+ mb();
+}
+
#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
static int msm_hsusb_reset(struct msm_hcd *mhcd)
{
@@ -649,6 +661,9 @@
writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
USB_PHY_CTRL2);
+ /* Reset USB PHY after performing USB Link RESET */
+ usb_phy_reset(mhcd);
+
msleep(100);
writel_relaxed(0x0, USB_AHBBURST);
@@ -1523,6 +1538,13 @@
goto disable_ldo;
}
+ pdata = mhcd->dev->platform_data;
+
+ if (pdata && pdata->use_sec_phy)
+ mhcd->usb_phy_ctrl_reg = USB_PHY_CTRL2;
+ else
+ mhcd->usb_phy_ctrl_reg = USB_PHY_CTRL;
+
ret = msm_hsusb_reset(mhcd);
if (ret) {
dev_err(&pdev->dev, "hsusb PHY initialization failed\n");
@@ -1535,7 +1557,6 @@
goto vbus_deinit;
}
- pdata = mhcd->dev->platform_data;
if (pdata && (!pdata->dock_connect_irq ||
!irq_read_line(pdata->dock_connect_irq)))
msm_ehci_vbus_power(mhcd, 1);
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 6d18d0f..c5adf38 100755
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -454,4 +454,5 @@
header-y += msm_audio_amrwbplus.h
header-y += avtimer.h
header-y += msm_ipa.h
+header-y += msm_pft.h
header-y += msm_thermal_ioctl.h
diff --git a/include/linux/msm_pft.h b/include/linux/msm_pft.h
new file mode 100644
index 0000000..4daf46b
--- /dev/null
+++ b/include/linux/msm_pft.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_PFT_H_
+#define MSM_PFT_H_
+
+#include <linux/types.h>
+
+/**
+ * enum pft_command_opcode - PFT driver command ID
+ *
+ * @PFT_CMD_OPCODE_SET_STATE -
+ * command ID to set PFT driver state
+ * @PFT_CMD_OPCODE_UPDATE_REG_APP_UID -
+ * command ID to update the list of registered application
+ * UID
+ * @PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC -
+ * command ID to perfrom in-place file encryption
+ */
+enum pft_command_opcode {
+ PFT_CMD_OPCODE_SET_STATE,
+ PFT_CMD_OPCODE_UPDATE_REG_APP_UID,
+ PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC,
+ /* */
+ PFT_CMD_OPCODE_MAX_COMMAND_INDEX
+};
+
+/**
+ * enum pft_state - PFT driver operational states
+ *
+ * @PFT_STATE_DEACTIVATED - driver is deativated.
+ * @PFT_STATE_DEACTIVATING - driver is in the process of being deativated.
+ * @PFT_STATE_KEY_REMOVED - driver is active but no encryption key is loaded.
+ * @PFT_STATE_REMOVING_KEY - driver is active, but the encryption key is being
+ * removed.
+ * @PFT_STATE_KEY_LOADED - driver is active, and the encryption key is loaded
+ * to encryption block, hence registered apps can perform file operations
+ * on encrypted files.
+ */
+enum pft_state {
+ PFT_STATE_DEACTIVATED,
+ PFT_STATE_DEACTIVATING,
+ PFT_STATE_KEY_REMOVED,
+ PFT_STATE_REMOVING_KEY,
+ PFT_STATE_KEY_LOADED,
+ /* Internal */
+ PFT_STATE_MAX_INDEX
+};
+
+/**
+ * enum pft_command_response_code - PFT response on the previous
+ * command
+ *
+ * @PFT_CMD_RESP_SUCCESS - The command was properly processed
+ * without an error.
+ * @PFT_CMD_RESP_GENERAL_ERROR -
+ * Indicates an error that cannot be better described by a
+ * more specific errors below.
+ * @PFT_CMD_RESP_INVALID_COMMAND - Invalid or unsupported
+ * command id.
+ * @PFT_CMD_RESP_INVALID_CMD_PARAMS - Invalid command
+ * parameters.
+ * @PFT_CMD_RESP_INVALID_STATE - Invalid state
+ * @PFT_CMD_RESP_ALREADY_IN_STATE - Used to indicates that
+ * the new state is equal to the existing one.
+ * @PFT_CMD_RESP_INPLACE_FILE_IS_OPEN - Used to indicates
+ * that the file that should be encrypted is already open
+ * and can be encrypted.
+ * @PFT_CMD_RESP_ENT_FILES_CLOSING_FAILURE
+ * Indicates about failure of the PFT to close Enterprise files
+ * @PFT_CMD_RESP_MAX_INDEX
+ */
+enum pft_command_response_code {
+ PFT_CMD_RESP_SUCCESS,
+ PFT_CMD_RESP_GENERAL_ERROR,
+ PFT_CMD_RESP_INVALID_COMMAND,
+ PFT_CMD_RESP_INVALID_CMD_PARAMS,
+ PFT_CMD_RESP_INVALID_STATE,
+ PFT_CMD_RESP_ALREADY_IN_STATE,
+ PFT_CMD_RESP_INPLACE_FILE_IS_OPEN,
+ PFT_CMD_RESP_ENT_FILES_CLOSING_FAILURE,
+ /* Internal */
+ PFT_CMD_RESP_MAX_INDEX
+};
+
+/**
+ * struct pft_command_response - response structure
+ *
+ * @command_id - see enum pft_command_response_code
+ * @error_codee - see enum pft_command_response_code
+ */
+struct pft_command_response {
+ __u32 command_id;
+ __u32 error_code;
+};
+
+/**
+ * struct pft_command - pft command
+ *
+ * @opcode - see enum pft_command_opcode.
+ * @set_state.state - see enum pft_state.
+ * @update_app_list.count - number of items in the
+ * registered applications list.
+ * @update_app_list.table - registered applications array
+ * @preform_in_place_file_enc.file_descriptor - file descriptor
+ * of the opened file to be in-placed encrypted.
+ */
+struct pft_command {
+ __u32 opcode;
+ union {
+ struct {
+ /* @see pft_state */
+ __u32 state;
+ } set_state;
+ struct {
+ __u32 items_count; /* number of items */
+ __u32 table[0]; /* array of UIDs */
+ } update_app_list;
+ struct {
+ __u32 file_descriptor;
+ } preform_in_place_file_enc;
+ };
+};
+
+#endif /* MSM_PFT_H_ */
diff --git a/include/linux/pft.h b/include/linux/pft.h
new file mode 100644
index 0000000..36e9612
--- /dev/null
+++ b/include/linux/pft.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFT_H_
+#define PFT_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#ifdef CONFIG_PFT
+
+/* dm-req-crypt API */
+int pft_get_key_index(struct inode *inode, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace);
+
+/* block layer API */
+bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2);
+
+/* --- security hooks , called from selinux --- */
+int pft_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+
+int pft_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode);
+
+int pft_file_open(struct file *filp, const struct cred *cred);
+
+int pft_file_permission(struct file *file, int mask);
+
+int pft_file_close(struct file *filp);
+
+int pft_inode_unlink(struct inode *dir, struct dentry *dentry);
+
+int pft_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ dev_t dev);
+
+int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry);
+
+int pft_inode_set_xattr(struct dentry *dentry, const char *name);
+
+
+#else
+static inline int pft_get_key_index(struct inode *inode, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace)
+{ return -ENODEV; }
+
+static inline bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{ return true; }
+
+static inline int pft_file_permission(struct file *file, int mask)
+{ return 0; }
+
+static inline int pft_inode_create(
+ struct inode *dir, struct dentry *dentry, umode_t mode)
+{ return 0; }
+
+static inline int pft_inode_post_create(
+ struct inode *dir, struct dentry *dentry, umode_t mode)
+{ return 0; }
+
+static inline int pft_file_open(struct file *filp, const struct cred *cred)
+{ return 0; }
+
+static inline int pft_file_close(struct file *filp)
+{ return 0; }
+
+static inline int pft_inode_unlink(struct inode *dir, struct dentry *dentry)
+{ return 0; }
+
+static inline int pft_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{ return 0; }
+
+static inline int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{ return 0; }
+
+static inline int pft_inode_set_xattr(struct dentry *dentry, const char *name)
+{ return 0; }
+
+#endif /* CONFIG_PFT */
+
+#endif /* PFT_H */
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 26c7eee..d160760 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -43,6 +43,7 @@
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
+ u32 tag; /* Per-File-Encryption tag */
struct mutex lock;
};
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 7c6f0ea..a5d42d5 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1703,25 +1703,22 @@
}
ret = dpcm_be_dai_prepare(fe, substream->stream);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
+ fe->dai_link->name);
goto out;
+ }
/* call prepare on the frontend */
if (!fe->fe_compr) {
ret = soc_pcm_prepare(substream);
if (ret < 0) {
- dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
+ dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
fe->dai_link->name);
goto out;
}
}
- ret = soc_pcm_prepare(substream);
- if (ret < 0) {
- dev_err(fe->dev,"dpcm: prepare FE %s failed\n", fe->dai_link->name);
- goto out;
- }
-
/* run the stream event for each BE */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
dpcm_dapm_stream_event(fe, stream,