Commit 3e84469d authored by Steve French's avatar Steve French

[CIFS] Add writepages support to shrink memory usage on writes,

eliminate the double copy, and improve cifs write performance and
help the server by upping the typical write size from 4K to 16K
(or even larger if wsize set explicitly)  for servers which support this.
Part 1 of 2
Signed-off-by: default avatarDave Kleikamp <shaggy@austin.ibm.com>
Signed-off-by: default avatarSteve French  <sfrench@us.ibm.com>
parent 70ca734a
......@@ -48,8 +48,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
struct smb_hdr * /* out */ ,
int * /* bytes returned */ , const int long_op);
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct smb_hdr * /* input */ , int hdr_len,
const char * /* SMB data to send */ , int data_len,
struct kvec *, int /* nvec */,
int * /* bytes returned */ , const int long_op);
extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
......@@ -241,7 +240,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count,
const __u64 offset, unsigned int *nbytes,
const char *buf,const int long_op);
struct kvec *iov, const int nvec, const int long_op);
extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
const unsigned char *searchName, __u64 * inode_number,
const struct nls_table *nls_codepage,
......
......@@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon
, nls_codepage);
up(&tcon->ses->sesSem);
/* BB FIXME add code to check if wsize needs
update due to negotiated smb buffer size
shrinking */
if(rc == 0)
atomic_inc(&tconInfoReconnectCount);
......@@ -220,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage);
up(&tcon->ses->sesSem);
/* BB FIXME add code to check if wsize needs
update due to negotiated smb buffer size
shrinking */
if(rc == 0)
atomic_inc(&tconInfoReconnectCount);
......@@ -1128,15 +1134,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
int
CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count,
const __u64 offset, unsigned int *nbytes, const char *buf,
const int long_op)
const __u64 offset, unsigned int *nbytes, struct kvec *iov,
int n_vec, const int long_op)
{
int rc = -EACCES;
WRITE_REQ *pSMB = NULL;
int bytes_returned;
int smb_hdr_len;
__u32 bytes_sent;
__u16 byte_count;
cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */
rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB);
......@@ -1154,31 +1158,20 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
pSMB->WriteMode = 0;
pSMB->Remaining = 0;
/* Can increase buffer size if buffer is big enough in some cases - ie
can send more if LARGE_WRITE_X capability returned by the server and if
our buffer is big enough or if we convert to iovecs on socket writes
and eliminate the copy to the CIFS buffer */
if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
} else {
bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
& ~0xFF;
}
if (bytes_sent > count)
bytes_sent = count;
pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */
pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
pSMB->hdr.smb_buf_length += bytes_sent+1;
pSMB->ByteCount = cpu_to_le16(byte_count);
pSMB->hdr.smb_buf_length += count+1;
pSMB->ByteCount = cpu_to_le16(count + 1);
iov[0].iov_base = pSMB;
iov[0].iov_len = smb_hdr_len + 4;
rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len,
buf, bytes_sent, &bytes_returned, long_op);
rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned,
long_op);
cifs_stats_inc(&tcon->num_writes);
if (rc) {
cFYI(1, ("Send error in write = %d", rc));
......
......@@ -1891,6 +1891,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
}
}
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
cifs_sb->wsize = min(cifs_sb->wsize,
(tcon->ses->server->maxBuf -
MAX_CIFS_HDR_SIZE));
}
/* volume_info.password is freed above when existing session found
......
......@@ -849,13 +849,19 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
/* BB FIXME We can not sign across two buffers yet */
if((experimEnabled) && ((pTcon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) {
struct kvec iov[2];
unsigned int len;
len = min(cifs_sb->wsize,
write_size - total_written);
/* iov[0] is reserved for smb header */
iov[1].iov_base = (char *)write_data +
total_written;
iov[1].iov_len = len;
rc = CIFSSMBWrite2(xid, pTcon,
open_file->netfid,
min_t(const int, cifs_sb->wsize,
write_size - total_written),
open_file->netfid, len,
*poffset, &bytes_written,
write_data + total_written,
long_op);
iov, 1, long_op);
} else
/* BB FIXME fixup indentation of line below */
#endif
......
......@@ -147,16 +147,19 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
Flags2 is converted in SendReceive */
smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
cFYI(1, ("Sending smb of length %d ", smb_buf_length));
cFYI(1, ("Sending smb of length %d", smb_buf_length));
dump_smb(smb_buffer, len);
while (len > 0) {
rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++;
if(i > 60) {
/* smaller timeout here than send2 since smaller size */
/* Although it may not be required, this also is smaller
oplock break time */
if(i > 30) {
cERROR(1,
("sends on sock %p stuck for 30 seconds",
("sends on sock %p stuck for 15 seconds",
ssocket));
rc = -EAGAIN;
break;
......@@ -172,7 +175,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
}
if (rc < 0) {
cERROR(1,("Error %d sending data on socket to server.", rc));
cERROR(1,("Error %d sending data on socket to server", rc));
} else {
rc = 0;
}
......@@ -182,22 +185,20 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
#ifdef CONFIG_CIFS_EXPERIMENTAL
static int
smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
unsigned int smb_hdr_length, const char * data, unsigned int datalen,
struct sockaddr *sin)
smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
struct sockaddr *sin)
{
int rc = 0;
int i = 0;
struct msghdr smb_msg;
struct kvec iov[2];
unsigned len = smb_hdr_length + 4;
struct smb_hdr *smb_buffer = iov[0].iov_base;
unsigned int len = iov[0].iov_len;
unsigned int total_len;
int first_vec = 0;
if(ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
iov[0].iov_base = smb_buffer;
iov[0].iov_len = len;
iov[1].iov_base = data;
iov[1].iov_len = datalen;
smb_msg.msg_name = sin;
smb_msg.msg_namelen = sizeof (struct sockaddr);
smb_msg.msg_control = NULL;
......@@ -209,18 +210,23 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
cifssmb.c and RFC1001 len is converted to bigendian in smb_send
Flags2 is converted in SendReceive */
total_len = 0;
for (i = 0; i < n_vec; i++)
total_len += iov[i].iov_len;
smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
cFYI(1, ("Sending smb: hdrlen %d datalen %d",
smb_hdr_length,datalen));
cFYI(1, ("Sending smb: total_len %d", total_len));
dump_smb(smb_buffer, len);
while (len + datalen > 0) {
rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len);
while (total_len) {
rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
n_vec - first_vec, total_len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++;
if(i > 60) {
if(i > 40) {
cERROR(1,
("sends on sock %p stuck for 30 seconds",
("sends on sock %p stuck for 20 seconds",
ssocket));
rc = -EAGAIN;
break;
......@@ -230,43 +236,52 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
}
if (rc < 0)
break;
if(iov[0].iov_len > 0) {
if(rc >= len) {
iov[0].iov_len = 0;
rc -= len;
len = 0;
} else { /* some of hdr was not sent */
len -= rc;
iov[0].iov_len -= rc;
iov[0].iov_base += rc;
continue;
}
if (rc >= total_len) {
WARN_ON(rc > total_len);
break;
}
if(rc == 0) {
/* should never happen, letting socket clear before
retrying is our only obvious option here */
cERROR(1,("tcp sent no data");
msleep(500);
continue;
}
if((iov[0].iov_len == 0) && (rc > 0)){
iov[1].iov_base += rc;
iov[1].iov_len -= rc;
datalen -= rc;
total_len -= rc;
for (i = first_vec; i < n_vec; i++) {
if (iov[i].iov_len) {
if (rc > iov[i].iov_len) {
rc -= iov[i].iov_len;
iov[i].iov_len = 0;
} else {
iov[i].iov_base += rc;
iov[i].iov_len -= rc;
first_vec = i;
break;
}
}
}
}
if (rc < 0) {
cERROR(1,("Error %d sending data on socket to server.", rc));
} else {
cERROR(1,("Error %d sending data on socket to server", rc));
} else
rc = 0;
}
return rc;
}
int
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
struct smb_hdr *in_buf, int hdrlen, const char * data,
int datalen, int *pbytes_returned, const int long_op)
struct kvec *iov, int n_vec, int *pbytes_returned,
const int long_op)
{
int rc = 0;
unsigned int receive_len;
unsigned long timeout;
struct mid_q_entry *midQ;
struct smb_hdr *in_buf = iov[0].iov_base;
if (ses == NULL) {
cERROR(1,("Null smb session"));
......@@ -364,7 +379,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */
midQ->midState = MID_REQUEST_SUBMITTED;
rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen,
rc = smb_send2(ses->server->ssocket, iov, n_vec,
(struct sockaddr *) &(ses->server->addr.sockAddr));
if(rc < 0) {
DeleteMidQEntry(midQ);
......
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