mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
ANDROID: goldfish_pipe: bugfixes and performance improvements.
Combine following patches from android-goldfish-3.18 branch: c0f015a [pipe] Fix the pipe driver for x64 platform + correct pages count 48e6bf5 [pipe] Use get_use_pages_fast() which is possibly faster fb20f13 [goldfish] More pages in goldfish pipe f180e6d goldfish_pipe: Return from read_write on signal and EIO 3dec3b7 [pipe] Fix a minor leak in setup_access_params_addr() Change-Id: I1041fd65d7faaec123e6cedd3dbbc5a2fbb86c4d
This commit is contained in:
committed by
Dmitry Shmidt
parent
2bed6160b3
commit
ba574b64bc
@@ -110,6 +110,16 @@
|
||||
#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
|
||||
#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
|
||||
|
||||
#define MAX_PAGES_TO_GRAB 32
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define DPRINT(...) { printk(KERN_ERR __VA_ARGS__); }
|
||||
#else
|
||||
#define DPRINT(...)
|
||||
#endif
|
||||
|
||||
struct access_params {
|
||||
unsigned long channel;
|
||||
u32 size;
|
||||
@@ -231,8 +241,10 @@ static int setup_access_params_addr(struct platform_device *pdev,
|
||||
if (valid_batchbuffer_addr(dev, aps)) {
|
||||
dev->aps = aps;
|
||||
return 0;
|
||||
} else
|
||||
} else {
|
||||
devm_kfree(&pdev->dev, aps);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* A value that will not be set by qemu emulator */
|
||||
@@ -269,6 +281,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
struct goldfish_pipe *pipe = filp->private_data;
|
||||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
unsigned long address, address_end;
|
||||
struct page* pages[MAX_PAGES_TO_GRAB] = {};
|
||||
int count = 0, ret = -EINVAL;
|
||||
|
||||
/* If the emulator already closed the pipe, no need to go further */
|
||||
@@ -293,45 +306,58 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
|
||||
while (address < address_end) {
|
||||
unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
|
||||
unsigned long next = page_end < address_end ? page_end
|
||||
: address_end;
|
||||
unsigned long avail = next - address;
|
||||
int status, wakeBit;
|
||||
struct page *page;
|
||||
|
||||
/* Either vaddr or paddr depending on the device version */
|
||||
unsigned long xaddr;
|
||||
unsigned long next, avail;
|
||||
int status, wakeBit, page_i, num_contiguous_pages;
|
||||
long first_page, last_page, requested_pages;
|
||||
unsigned long xaddr, xaddr_prev, xaddr_i;
|
||||
|
||||
/*
|
||||
* We grab the pages on a page-by-page basis in case user
|
||||
* space gives us a potentially huge buffer but the read only
|
||||
* returns a small amount, then there's no need to pin that
|
||||
* much memory to the process.
|
||||
* Attempt to grab multiple physically contiguous pages.
|
||||
*/
|
||||
down_read(¤t->mm->mmap_sem);
|
||||
ret = get_user_pages(address, 1, is_write ? 0 : FOLL_WRITE,
|
||||
&page, NULL);
|
||||
up_read(¤t->mm->mmap_sem);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (dev->version) {
|
||||
/* Device version 1 or newer (qemu-android) expects the
|
||||
* physical address.
|
||||
*/
|
||||
xaddr = page_to_phys(page) | (address & ~PAGE_MASK);
|
||||
} else {
|
||||
/* Device version 0 (classic emulator) expects the
|
||||
* virtual address.
|
||||
*/
|
||||
xaddr = address;
|
||||
first_page = address & PAGE_MASK;
|
||||
last_page = (address_end - 1) & PAGE_MASK;
|
||||
requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
|
||||
if (requested_pages > MAX_PAGES_TO_GRAB) {
|
||||
requested_pages = MAX_PAGES_TO_GRAB;
|
||||
}
|
||||
ret = get_user_pages_fast(first_page, requested_pages,
|
||||
!is_write, pages);
|
||||
|
||||
DPRINT("%s: requested pages: %d %d\n", __FUNCTION__, ret, requested_pages);
|
||||
if (ret == 0) {
|
||||
DPRINT("%s: error: (requested pages == 0) (wanted %d)\n",
|
||||
__FUNCTION__, requested_pages);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0) {
|
||||
DPRINT("%s: (requested pages < 0) %d \n",
|
||||
__FUNCTION__, requested_pages);
|
||||
return ret;
|
||||
}
|
||||
|
||||
xaddr = page_to_phys(pages[0]) | (address & ~PAGE_MASK);
|
||||
xaddr_prev = xaddr;
|
||||
num_contiguous_pages = ret == 0 ? 0 : 1;
|
||||
for (page_i = 1; page_i < ret; page_i++) {
|
||||
xaddr_i = page_to_phys(pages[page_i]) | (address & ~PAGE_MASK);
|
||||
if (xaddr_i == xaddr_prev + PAGE_SIZE) {
|
||||
page_end += PAGE_SIZE;
|
||||
xaddr_prev = xaddr_i;
|
||||
num_contiguous_pages++;
|
||||
} else {
|
||||
DPRINT("%s: discontinuous page boundary: %d pages instead\n",
|
||||
__FUNCTION__, page_i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
next = page_end < address_end ? page_end : address_end;
|
||||
avail = next - address;
|
||||
|
||||
/* Now, try to transfer the bytes in the current page */
|
||||
spin_lock_irqsave(&dev->lock, irq_flags);
|
||||
if (access_with_param(dev,
|
||||
is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
|
||||
xaddr, avail, pipe, &status)) {
|
||||
is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
|
||||
xaddr, avail, pipe, &status)) {
|
||||
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
writel(avail, dev->base + PIPE_REG_SIZE);
|
||||
@@ -344,9 +370,13 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, irq_flags);
|
||||
|
||||
if (status > 0 && !is_write)
|
||||
set_page_dirty(page);
|
||||
put_page(page);
|
||||
for (page_i = 0; page_i < ret; page_i++) {
|
||||
if (status > 0 && !is_write &&
|
||||
page_i < num_contiguous_pages) {
|
||||
set_page_dirty(pages[page_i]);
|
||||
}
|
||||
put_page(pages[page_i]);
|
||||
}
|
||||
|
||||
if (status > 0) { /* Correct transfer */
|
||||
count += status;
|
||||
@@ -368,7 +398,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
*/
|
||||
if (status != PIPE_ERROR_AGAIN)
|
||||
pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n",
|
||||
status, is_write ? "write" : "read");
|
||||
status, is_write ? "write" : "read");
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
@@ -378,7 +408,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
* non-blocking mode, just return the error code.
|
||||
*/
|
||||
if (status != PIPE_ERROR_AGAIN ||
|
||||
(filp->f_flags & O_NONBLOCK) != 0) {
|
||||
(filp->f_flags & O_NONBLOCK) != 0) {
|
||||
ret = goldfish_pipe_error_convert(status);
|
||||
break;
|
||||
}
|
||||
@@ -392,7 +422,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
|
||||
/* Tell the emulator we're going to wait for a wake event */
|
||||
goldfish_cmd(pipe,
|
||||
is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
|
||||
is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
|
||||
|
||||
/* Unlock the pipe, then wait for the wake signal */
|
||||
mutex_unlock(&pipe->lock);
|
||||
@@ -538,6 +568,8 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file)
|
||||
|
||||
pipe->dev = dev;
|
||||
mutex_init(&pipe->lock);
|
||||
DPRINT("%s: call. pipe_dev pipe_dev=0x%lx new_pipe_addr=0x%lx file=0x%lx\n", __FUNCTION__, pipe_dev, pipe, file);
|
||||
// spin lock init, write head of list, i guess
|
||||
init_waitqueue_head(&pipe->wake_queue);
|
||||
|
||||
/*
|
||||
@@ -560,6 +592,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct goldfish_pipe *pipe = filp->private_data;
|
||||
|
||||
DPRINT("%s: call. pipe=0x%lx file=0x%lx\n", __FUNCTION__, pipe, filp);
|
||||
/* The guest is closing the channel, so tell the emulator right now */
|
||||
goldfish_cmd(pipe, CMD_CLOSE);
|
||||
kfree(pipe);
|
||||
@@ -584,6 +617,7 @@ static struct miscdevice goldfish_pipe_device = {
|
||||
|
||||
static int goldfish_pipe_probe(struct platform_device *pdev)
|
||||
{
|
||||
DPRINT("%s: call. platform_device=0x%lx\n", __FUNCTION__, pdev);
|
||||
int err;
|
||||
struct resource *r;
|
||||
struct goldfish_pipe_dev *dev = pipe_dev;
|
||||
|
||||
Reference in New Issue
Block a user