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 *, ...@@ -48,8 +48,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
struct smb_hdr * /* out */ , struct smb_hdr * /* out */ ,
int * /* bytes returned */ , const int long_op); int * /* bytes returned */ , const int long_op);
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct smb_hdr * /* input */ , int hdr_len, struct kvec *, int /* nvec */,
const char * /* SMB data to send */ , int data_len,
int * /* bytes returned */ , const int long_op); int * /* bytes returned */ , const int long_op);
extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
...@@ -241,7 +240,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, ...@@ -241,7 +240,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count, const int netfid, const unsigned int count,
const __u64 offset, unsigned int *nbytes, 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, extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
const unsigned char *searchName, __u64 * inode_number, const unsigned char *searchName, __u64 * inode_number,
const struct nls_table *nls_codepage, const struct nls_table *nls_codepage,
......
...@@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, ...@@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon
, nls_codepage); , nls_codepage);
up(&tcon->ses->sesSem); up(&tcon->ses->sesSem);
/* BB FIXME add code to check if wsize needs
update due to negotiated smb buffer size
shrinking */
if(rc == 0) if(rc == 0)
atomic_inc(&tconInfoReconnectCount); atomic_inc(&tconInfoReconnectCount);
...@@ -220,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, ...@@ -220,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
rc = CIFSTCon(0, tcon->ses, tcon->treeName, rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage); tcon, nls_codepage);
up(&tcon->ses->sesSem); up(&tcon->ses->sesSem);
/* BB FIXME add code to check if wsize needs
update due to negotiated smb buffer size
shrinking */
if(rc == 0) if(rc == 0)
atomic_inc(&tconInfoReconnectCount); atomic_inc(&tconInfoReconnectCount);
...@@ -1128,15 +1134,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, ...@@ -1128,15 +1134,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
int int
CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count, const int netfid, const unsigned int count,
const __u64 offset, unsigned int *nbytes, const char *buf, const __u64 offset, unsigned int *nbytes, struct kvec *iov,
const int long_op) int n_vec, const int long_op)
{ {
int rc = -EACCES; int rc = -EACCES;
WRITE_REQ *pSMB = NULL; WRITE_REQ *pSMB = NULL;
int bytes_returned; int bytes_returned;
int smb_hdr_len; int smb_hdr_len;
__u32 bytes_sent;
__u16 byte_count;
cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */
rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB);
...@@ -1154,31 +1158,20 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, ...@@ -1154,31 +1158,20 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
pSMB->WriteMode = 0; pSMB->WriteMode = 0;
pSMB->Remaining = 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 = pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); 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(count & 0xFFFF);
pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
pSMB->hdr.smb_buf_length += bytes_sent+1; pSMB->hdr.smb_buf_length += count+1;
pSMB->ByteCount = cpu_to_le16(byte_count); 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, rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned,
buf, bytes_sent, &bytes_returned, long_op); long_op);
cifs_stats_inc(&tcon->num_writes); cifs_stats_inc(&tcon->num_writes);
if (rc) { if (rc) {
cFYI(1, ("Send error in write = %d", 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, ...@@ -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 /* 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, ...@@ -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 */ /* BB FIXME We can not sign across two buffers yet */
if((experimEnabled) && ((pTcon->ses->server->secMode & if((experimEnabled) && ((pTcon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { (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, rc = CIFSSMBWrite2(xid, pTcon,
open_file->netfid, open_file->netfid, len,
min_t(const int, cifs_sb->wsize,
write_size - total_written),
*poffset, &bytes_written, *poffset, &bytes_written,
write_data + total_written, iov, 1, long_op);
long_op);
} else } else
/* BB FIXME fixup indentation of line below */ /* BB FIXME fixup indentation of line below */
#endif #endif
......
...@@ -147,16 +147,19 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, ...@@ -147,16 +147,19 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
Flags2 is converted in SendReceive */ Flags2 is converted in SendReceive */
smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); 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); dump_smb(smb_buffer, len);
while (len > 0) { while (len > 0) {
rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) { if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++; 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, cERROR(1,
("sends on sock %p stuck for 30 seconds", ("sends on sock %p stuck for 15 seconds",
ssocket)); ssocket));
rc = -EAGAIN; rc = -EAGAIN;
break; break;
...@@ -172,7 +175,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, ...@@ -172,7 +175,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
} }
if (rc < 0) { 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 { } else {
rc = 0; rc = 0;
} }
...@@ -182,22 +185,20 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, ...@@ -182,22 +185,20 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
static int static int
smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
unsigned int smb_hdr_length, const char * data, unsigned int datalen, struct sockaddr *sin)
struct sockaddr *sin)
{ {
int rc = 0; int rc = 0;
int i = 0; int i = 0;
struct msghdr smb_msg; struct msghdr smb_msg;
struct kvec iov[2]; struct smb_hdr *smb_buffer = iov[0].iov_base;
unsigned len = smb_hdr_length + 4; unsigned int len = iov[0].iov_len;
unsigned int total_len;
int first_vec = 0;
if(ssocket == NULL) if(ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */ 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_name = sin;
smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_namelen = sizeof (struct sockaddr);
smb_msg.msg_control = NULL; smb_msg.msg_control = NULL;
...@@ -209,18 +210,23 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, ...@@ -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 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
Flags2 is converted in SendReceive */ 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); smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
cFYI(1, ("Sending smb: hdrlen %d datalen %d", cFYI(1, ("Sending smb: total_len %d", total_len));
smb_hdr_length,datalen));
dump_smb(smb_buffer, len); dump_smb(smb_buffer, len);
while (len + datalen > 0) { while (total_len) {
rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
n_vec - first_vec, total_len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) { if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++; i++;
if(i > 60) { if(i > 40) {
cERROR(1, cERROR(1,
("sends on sock %p stuck for 30 seconds", ("sends on sock %p stuck for 20 seconds",
ssocket)); ssocket));
rc = -EAGAIN; rc = -EAGAIN;
break; break;
...@@ -230,43 +236,52 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, ...@@ -230,43 +236,52 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
} }
if (rc < 0) if (rc < 0)
break; break;
if(iov[0].iov_len > 0) {
if(rc >= len) { if (rc >= total_len) {
iov[0].iov_len = 0; WARN_ON(rc > total_len);
rc -= len; break;
len = 0; }
} else { /* some of hdr was not sent */ if(rc == 0) {
len -= rc; /* should never happen, letting socket clear before
iov[0].iov_len -= rc; retrying is our only obvious option here */
iov[0].iov_base += rc; cERROR(1,("tcp sent no data");
continue; msleep(500);
} continue;
} }
if((iov[0].iov_len == 0) && (rc > 0)){ total_len -= rc;
iov[1].iov_base += rc; for (i = first_vec; i < n_vec; i++) {
iov[1].iov_len -= rc; if (iov[i].iov_len) {
datalen -= rc; 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) { 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 { } else
rc = 0; rc = 0;
}
return rc; return rc;
} }
int int
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
struct smb_hdr *in_buf, int hdrlen, const char * data, struct kvec *iov, int n_vec, int *pbytes_returned,
int datalen, int *pbytes_returned, const int long_op) const int long_op)
{ {
int rc = 0; int rc = 0;
unsigned int receive_len; unsigned int receive_len;
unsigned long timeout; unsigned long timeout;
struct mid_q_entry *midQ; struct mid_q_entry *midQ;
struct smb_hdr *in_buf = iov[0].iov_base;
if (ses == NULL) { if (ses == NULL) {
cERROR(1,("Null smb session")); cERROR(1,("Null smb session"));
...@@ -364,7 +379,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -364,7 +379,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ /* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */
midQ->midState = MID_REQUEST_SUBMITTED; 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)); (struct sockaddr *) &(ses->server->addr.sockAddr));
if(rc < 0) { if(rc < 0) {
DeleteMidQEntry(midQ); 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