aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve French <sfrench@us.ibm.com>2005-10-03 16:37:24 -0400
committerSteve French <sfrench@us.ibm.com>2005-10-03 16:37:24 -0400
commit3e84469d0101456caceffc6b22218a49017fcd3f (patch)
tree2e52687ade7f3e52b5621142997ca6e7b38df70a
parent70ca734a14366b634224a1e4586d43b36b65ab67 (diff)
[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: Dave Kleikamp <shaggy@austin.ibm.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifsproto.h5
-rw-r--r--fs/cifs/cifssmb.c41
-rw-r--r--fs/cifs/connect.c4
-rw-r--r--fs/cifs/file.c16
-rw-r--r--fs/cifs/transport.c95
5 files changed, 89 insertions, 72 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index dc5a6a6ff2f9..fb3e76043c50 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -48,8 +48,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
48 struct smb_hdr * /* out */ , 48 struct smb_hdr * /* out */ ,
49 int * /* bytes returned */ , const int long_op); 49 int * /* bytes returned */ , const int long_op);
50extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, 50extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
51 struct smb_hdr * /* input */ , int hdr_len, 51 struct kvec *, int /* nvec */,
52 const char * /* SMB data to send */ , int data_len,
53 int * /* bytes returned */ , const int long_op); 52 int * /* bytes returned */ , const int long_op);
54extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); 53extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
55extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); 54extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
@@ -241,7 +240,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
241extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, 240extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
242 const int netfid, const unsigned int count, 241 const int netfid, const unsigned int count,
243 const __u64 offset, unsigned int *nbytes, 242 const __u64 offset, unsigned int *nbytes,
244 const char *buf,const int long_op); 243 struct kvec *iov, const int nvec, const int long_op);
245extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, 244extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
246 const unsigned char *searchName, __u64 * inode_number, 245 const unsigned char *searchName, __u64 * inode_number,
247 const struct nls_table *nls_codepage, 246 const struct nls_table *nls_codepage,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 52caac063a77..365949c14646 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
125 rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon 125 rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon
126 , nls_codepage); 126 , nls_codepage);
127 up(&tcon->ses->sesSem); 127 up(&tcon->ses->sesSem);
128 /* BB FIXME add code to check if wsize needs
129 update due to negotiated smb buffer size
130 shrinking */
128 if(rc == 0) 131 if(rc == 0)
129 atomic_inc(&tconInfoReconnectCount); 132 atomic_inc(&tconInfoReconnectCount);
130 133
@@ -220,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
220 rc = CIFSTCon(0, tcon->ses, tcon->treeName, 223 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
221 tcon, nls_codepage); 224 tcon, nls_codepage);
222 up(&tcon->ses->sesSem); 225 up(&tcon->ses->sesSem);
226 /* BB FIXME add code to check if wsize needs
227 update due to negotiated smb buffer size
228 shrinking */
223 if(rc == 0) 229 if(rc == 0)
224 atomic_inc(&tconInfoReconnectCount); 230 atomic_inc(&tconInfoReconnectCount);
225 231
@@ -1128,15 +1134,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1128int 1134int
1129CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, 1135CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
1130 const int netfid, const unsigned int count, 1136 const int netfid, const unsigned int count,
1131 const __u64 offset, unsigned int *nbytes, const char *buf, 1137 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1132 const int long_op) 1138 int n_vec, const int long_op)
1133{ 1139{
1134 int rc = -EACCES; 1140 int rc = -EACCES;
1135 WRITE_REQ *pSMB = NULL; 1141 WRITE_REQ *pSMB = NULL;
1136 int bytes_returned; 1142 int bytes_returned;
1137 int smb_hdr_len; 1143 int smb_hdr_len;
1138 __u32 bytes_sent;
1139 __u16 byte_count;
1140 1144
1141 cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ 1145 cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */
1142 rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); 1146 rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB);
@@ -1154,31 +1158,20 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
1154 pSMB->WriteMode = 0; 1158 pSMB->WriteMode = 0;
1155 pSMB->Remaining = 0; 1159 pSMB->Remaining = 0;
1156 1160
1157 /* Can increase buffer size if buffer is big enough in some cases - ie
1158 can send more if LARGE_WRITE_X capability returned by the server and if
1159 our buffer is big enough or if we convert to iovecs on socket writes
1160 and eliminate the copy to the CIFS buffer */
1161 if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
1162 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1163 } else {
1164 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1165 & ~0xFF;
1166 }
1167
1168 if (bytes_sent > count)
1169 bytes_sent = count;
1170 pSMB->DataOffset = 1161 pSMB->DataOffset =
1171 cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); 1162 cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
1172 1163
1173 byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */ 1164 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1174 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); 1165 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
1175 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
1176 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ 1166 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
1177 pSMB->hdr.smb_buf_length += bytes_sent+1; 1167 pSMB->hdr.smb_buf_length += count+1;
1178 pSMB->ByteCount = cpu_to_le16(byte_count); 1168 pSMB->ByteCount = cpu_to_le16(count + 1);
1169
1170 iov[0].iov_base = pSMB;
1171 iov[0].iov_len = smb_hdr_len + 4;
1179 1172
1180 rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, 1173 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned,
1181 buf, bytes_sent, &bytes_returned, long_op); 1174 long_op);
1182 cifs_stats_inc(&tcon->num_writes); 1175 cifs_stats_inc(&tcon->num_writes);
1183 if (rc) { 1176 if (rc) {
1184 cFYI(1, ("Send error in write = %d", rc)); 1177 cFYI(1, ("Send error in write = %d", rc));
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index e27e5ad8b591..f05d9e2016d5 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1891,6 +1891,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
1891 } 1891 }
1892 } 1892 }
1893 } 1893 }
1894 if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
1895 cifs_sb->wsize = min(cifs_sb->wsize,
1896 (tcon->ses->server->maxBuf -
1897 MAX_CIFS_HDR_SIZE));
1894 } 1898 }
1895 1899
1896 /* volume_info.password is freed above when existing session found 1900 /* volume_info.password is freed above when existing session found
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 3766db2bb7f2..941108352547 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -849,13 +849,19 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
849 /* BB FIXME We can not sign across two buffers yet */ 849 /* BB FIXME We can not sign across two buffers yet */
850 if((experimEnabled) && ((pTcon->ses->server->secMode & 850 if((experimEnabled) && ((pTcon->ses->server->secMode &
851 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { 851 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) {
852 struct kvec iov[2];
853 unsigned int len;
854
855 len = min(cifs_sb->wsize,
856 write_size - total_written);
857 /* iov[0] is reserved for smb header */
858 iov[1].iov_base = (char *)write_data +
859 total_written;
860 iov[1].iov_len = len;
852 rc = CIFSSMBWrite2(xid, pTcon, 861 rc = CIFSSMBWrite2(xid, pTcon,
853 open_file->netfid, 862 open_file->netfid, len,
854 min_t(const int, cifs_sb->wsize,
855 write_size - total_written),
856 *poffset, &bytes_written, 863 *poffset, &bytes_written,
857 write_data + total_written, 864 iov, 1, long_op);
858 long_op);
859 } else 865 } else
860 /* BB FIXME fixup indentation of line below */ 866 /* BB FIXME fixup indentation of line below */
861#endif 867#endif
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 9e8e85a8d186..38b3b2463ae4 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -147,16 +147,19 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
147 Flags2 is converted in SendReceive */ 147 Flags2 is converted in SendReceive */
148 148
149 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); 149 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
150 cFYI(1, ("Sending smb of length %d ", smb_buf_length)); 150 cFYI(1, ("Sending smb of length %d", smb_buf_length));
151 dump_smb(smb_buffer, len); 151 dump_smb(smb_buffer, len);
152 152
153 while (len > 0) { 153 while (len > 0) {
154 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); 154 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
155 if ((rc == -ENOSPC) || (rc == -EAGAIN)) { 155 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
156 i++; 156 i++;
157 if(i > 60) { 157 /* smaller timeout here than send2 since smaller size */
158 /* Although it may not be required, this also is smaller
159 oplock break time */
160 if(i > 30) {
158 cERROR(1, 161 cERROR(1,
159 ("sends on sock %p stuck for 30 seconds", 162 ("sends on sock %p stuck for 15 seconds",
160 ssocket)); 163 ssocket));
161 rc = -EAGAIN; 164 rc = -EAGAIN;
162 break; 165 break;
@@ -172,7 +175,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
172 } 175 }
173 176
174 if (rc < 0) { 177 if (rc < 0) {
175 cERROR(1,("Error %d sending data on socket to server.", rc)); 178 cERROR(1,("Error %d sending data on socket to server", rc));
176 } else { 179 } else {
177 rc = 0; 180 rc = 0;
178 } 181 }
@@ -182,22 +185,20 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
182 185
183#ifdef CONFIG_CIFS_EXPERIMENTAL 186#ifdef CONFIG_CIFS_EXPERIMENTAL
184static int 187static int
185smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, 188smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
186 unsigned int smb_hdr_length, const char * data, unsigned int datalen, 189 struct sockaddr *sin)
187 struct sockaddr *sin)
188{ 190{
189 int rc = 0; 191 int rc = 0;
190 int i = 0; 192 int i = 0;
191 struct msghdr smb_msg; 193 struct msghdr smb_msg;
192 struct kvec iov[2]; 194 struct smb_hdr *smb_buffer = iov[0].iov_base;
193 unsigned len = smb_hdr_length + 4; 195 unsigned int len = iov[0].iov_len;
196 unsigned int total_len;
197 int first_vec = 0;
194 198
195 if(ssocket == NULL) 199 if(ssocket == NULL)
196 return -ENOTSOCK; /* BB eventually add reconnect code here */ 200 return -ENOTSOCK; /* BB eventually add reconnect code here */
197 iov[0].iov_base = smb_buffer; 201
198 iov[0].iov_len = len;
199 iov[1].iov_base = data;
200 iov[1].iov_len = datalen;
201 smb_msg.msg_name = sin; 202 smb_msg.msg_name = sin;
202 smb_msg.msg_namelen = sizeof (struct sockaddr); 203 smb_msg.msg_namelen = sizeof (struct sockaddr);
203 smb_msg.msg_control = NULL; 204 smb_msg.msg_control = NULL;
@@ -209,18 +210,23 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
209 cifssmb.c and RFC1001 len is converted to bigendian in smb_send 210 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
210 Flags2 is converted in SendReceive */ 211 Flags2 is converted in SendReceive */
211 212
213
214 total_len = 0;
215 for (i = 0; i < n_vec; i++)
216 total_len += iov[i].iov_len;
217
212 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); 218 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
213 cFYI(1, ("Sending smb: hdrlen %d datalen %d", 219 cFYI(1, ("Sending smb: total_len %d", total_len));
214 smb_hdr_length,datalen));
215 dump_smb(smb_buffer, len); 220 dump_smb(smb_buffer, len);
216 221
217 while (len + datalen > 0) { 222 while (total_len) {
218 rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); 223 rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
224 n_vec - first_vec, total_len);
219 if ((rc == -ENOSPC) || (rc == -EAGAIN)) { 225 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
220 i++; 226 i++;
221 if(i > 60) { 227 if(i > 40) {
222 cERROR(1, 228 cERROR(1,
223 ("sends on sock %p stuck for 30 seconds", 229 ("sends on sock %p stuck for 20 seconds",
224 ssocket)); 230 ssocket));
225 rc = -EAGAIN; 231 rc = -EAGAIN;
226 break; 232 break;
@@ -230,43 +236,52 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
230 } 236 }
231 if (rc < 0) 237 if (rc < 0)
232 break; 238 break;
233 if(iov[0].iov_len > 0) { 239
234 if(rc >= len) { 240 if (rc >= total_len) {
235 iov[0].iov_len = 0; 241 WARN_ON(rc > total_len);
236 rc -= len; 242 break;
237 len = 0; 243 }
238 } else { /* some of hdr was not sent */ 244 if(rc == 0) {
239 len -= rc; 245 /* should never happen, letting socket clear before
240 iov[0].iov_len -= rc; 246 retrying is our only obvious option here */
241 iov[0].iov_base += rc; 247 cERROR(1,("tcp sent no data");
242 continue; 248 msleep(500);
243 } 249 continue;
244 } 250 }
245 if((iov[0].iov_len == 0) && (rc > 0)){ 251 total_len -= rc;
246 iov[1].iov_base += rc; 252 for (i = first_vec; i < n_vec; i++) {
247 iov[1].iov_len -= rc; 253 if (iov[i].iov_len) {
248 datalen -= rc; 254 if (rc > iov[i].iov_len) {
255 rc -= iov[i].iov_len;
256 iov[i].iov_len = 0;
257 } else {
258 iov[i].iov_base += rc;
259 iov[i].iov_len -= rc;
260 first_vec = i;
261 break;
262 }
263 }
249 } 264 }
250 } 265 }
251 266
252 if (rc < 0) { 267 if (rc < 0) {
253 cERROR(1,("Error %d sending data on socket to server.", rc)); 268 cERROR(1,("Error %d sending data on socket to server", rc));
254 } else { 269 } else
255 rc = 0; 270 rc = 0;
256 }
257 271
258 return rc; 272 return rc;
259} 273}
260 274
261int 275int
262SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, 276SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
263 struct smb_hdr *in_buf, int hdrlen, const char * data, 277 struct kvec *iov, int n_vec, int *pbytes_returned,
264 int datalen, int *pbytes_returned, const int long_op) 278 const int long_op)
265{ 279{
266 int rc = 0; 280 int rc = 0;
267 unsigned int receive_len; 281 unsigned int receive_len;
268 unsigned long timeout; 282 unsigned long timeout;
269 struct mid_q_entry *midQ; 283 struct mid_q_entry *midQ;
284 struct smb_hdr *in_buf = iov[0].iov_base;
270 285
271 if (ses == NULL) { 286 if (ses == NULL) {
272 cERROR(1,("Null smb session")); 287 cERROR(1,("Null smb session"));
@@ -364,7 +379,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
364/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ 379/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */
365 380
366 midQ->midState = MID_REQUEST_SUBMITTED; 381 midQ->midState = MID_REQUEST_SUBMITTED;
367 rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen, 382 rc = smb_send2(ses->server->ssocket, iov, n_vec,
368 (struct sockaddr *) &(ses->server->addr.sockAddr)); 383 (struct sockaddr *) &(ses->server->addr.sockAddr));
369 if(rc < 0) { 384 if(rc < 0) {
370 DeleteMidQEntry(midQ); 385 DeleteMidQEntry(midQ);