fsi: sbefifo: Bump max command length

Otherwise cronus putmem fails istep and BML fails to upload skiboot

To do that, we still use our one-page command buffer for small commands
for speed, and for anything bigger, with a limit of 1MB plus a page,
we vmalloc a temporary buffer.

The limit was chosen because Cronus will break up any data transfer
into 1M chunks (the extra page is for the command header).
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Reviewed-by: default avatarAndrew Jeffery <andrew@aj.id.au>
parent aa1221b2
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mm.h>
/* /*
* The SBEFIFO is a pipe-like FSI device for communicating with * The SBEFIFO is a pipe-like FSI device for communicating with
...@@ -110,7 +111,7 @@ enum sbe_state ...@@ -110,7 +111,7 @@ enum sbe_state
#define SBEFIFO_TIMEOUT_IN_RSP 1000 #define SBEFIFO_TIMEOUT_IN_RSP 1000
/* Other constants */ /* Other constants */
#define SBEFIFO_MAX_CMD_LEN PAGE_SIZE #define SBEFIFO_MAX_USER_CMD_LEN (0x100000 + PAGE_SIZE)
#define SBEFIFO_RESET_MAGIC 0x52534554 /* "RSET" */ #define SBEFIFO_RESET_MAGIC 0x52534554 /* "RSET" */
struct sbefifo { struct sbefifo {
...@@ -128,6 +129,7 @@ struct sbefifo { ...@@ -128,6 +129,7 @@ struct sbefifo {
struct sbefifo_user { struct sbefifo_user {
struct sbefifo *sbefifo; struct sbefifo *sbefifo;
struct mutex file_lock; struct mutex file_lock;
void *cmd_page;
void *pending_cmd; void *pending_cmd;
size_t pending_len; size_t pending_len;
}; };
...@@ -726,7 +728,7 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len, ...@@ -726,7 +728,7 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
return -ENODEV; return -ENODEV;
if (WARN_ON_ONCE(sbefifo->magic != SBEFIFO_MAGIC)) if (WARN_ON_ONCE(sbefifo->magic != SBEFIFO_MAGIC))
return -ENODEV; return -ENODEV;
if (!resp_len || !command || !response || cmd_len > SBEFIFO_MAX_CMD_LEN) if (!resp_len || !command || !response)
return -EINVAL; return -EINVAL;
/* Prepare iov iterator */ /* Prepare iov iterator */
...@@ -751,6 +753,15 @@ EXPORT_SYMBOL_GPL(sbefifo_submit); ...@@ -751,6 +753,15 @@ EXPORT_SYMBOL_GPL(sbefifo_submit);
/* /*
* Char device interface * Char device interface
*/ */
static void sbefifo_release_command(struct sbefifo_user *user)
{
if (is_vmalloc_addr(user->pending_cmd))
vfree(user->pending_cmd);
user->pending_cmd = NULL;
user->pending_len = 0;
}
static int sbefifo_user_open(struct inode *inode, struct file *file) static int sbefifo_user_open(struct inode *inode, struct file *file)
{ {
struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev); struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev);
...@@ -762,8 +773,8 @@ static int sbefifo_user_open(struct inode *inode, struct file *file) ...@@ -762,8 +773,8 @@ static int sbefifo_user_open(struct inode *inode, struct file *file)
file->private_data = user; file->private_data = user;
user->sbefifo = sbefifo; user->sbefifo = sbefifo;
user->pending_cmd = (void *)__get_free_page(GFP_KERNEL); user->cmd_page = (void *)__get_free_page(GFP_KERNEL);
if (!user->pending_cmd) { if (!user->cmd_page) {
kfree(user); kfree(user);
return -ENOMEM; return -ENOMEM;
} }
...@@ -816,7 +827,7 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf, ...@@ -816,7 +827,7 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
/* Extract the response length */ /* Extract the response length */
rc = len - iov_iter_count(&resp_iter); rc = len - iov_iter_count(&resp_iter);
bail: bail:
user->pending_len = 0; sbefifo_release_command(user);
mutex_unlock(&user->file_lock); mutex_unlock(&user->file_lock);
return rc; return rc;
} }
...@@ -831,13 +842,23 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf, ...@@ -831,13 +842,23 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
if (!user) if (!user)
return -EINVAL; return -EINVAL;
sbefifo = user->sbefifo; sbefifo = user->sbefifo;
if (len > SBEFIFO_MAX_CMD_LEN) if (len > SBEFIFO_MAX_USER_CMD_LEN)
return -EINVAL; return -EINVAL;
if (len & 3) if (len & 3)
return -EINVAL; return -EINVAL;
mutex_lock(&user->file_lock); mutex_lock(&user->file_lock);
/* Can we use the pre-allocate buffer ? If not, allocate */
if (len <= PAGE_SIZE)
user->pending_cmd = user->cmd_page;
else
user->pending_cmd = vmalloc(len);
if (!user->pending_cmd) {
rc = -ENOMEM;
goto bail;
}
/* Copy the command into the staging buffer */ /* Copy the command into the staging buffer */
if (copy_from_user(user->pending_cmd, buf, len)) { if (copy_from_user(user->pending_cmd, buf, len)) {
rc = -EFAULT; rc = -EFAULT;
...@@ -863,6 +884,9 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf, ...@@ -863,6 +884,9 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
/* Update the staging buffer size */ /* Update the staging buffer size */
user->pending_len = len; user->pending_len = len;
bail: bail:
if (!user->pending_len)
sbefifo_release_command(user);
mutex_unlock(&user->file_lock); mutex_unlock(&user->file_lock);
/* And that's it, we'll issue the command on a read */ /* And that's it, we'll issue the command on a read */
...@@ -876,7 +900,8 @@ static int sbefifo_user_release(struct inode *inode, struct file *file) ...@@ -876,7 +900,8 @@ static int sbefifo_user_release(struct inode *inode, struct file *file)
if (!user) if (!user)
return -EINVAL; return -EINVAL;
free_page((unsigned long)user->pending_cmd); sbefifo_release_command(user);
free_page((unsigned long)user->cmd_page);
kfree(user); kfree(user);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment