diff options
author | Steve French <sfrench@us.ibm.com> | 2005-10-03 16:37:24 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2005-10-03 16:37:24 -0400 |
commit | 3e84469d0101456caceffc6b22218a49017fcd3f (patch) | |
tree | 2e52687ade7f3e52b5621142997ca6e7b38df70a | |
parent | 70ca734a14366b634224a1e4586d43b36b65ab67 (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.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 41 | ||||
-rw-r--r-- | fs/cifs/connect.c | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 16 | ||||
-rw-r--r-- | fs/cifs/transport.c | 95 |
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); |
50 | extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, | 50 | extern 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); |
54 | extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); | 53 | extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); |
55 | extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); | 54 | 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 | extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, | 240 | extern 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); |
245 | extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, | 244 | extern 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, | |||
1128 | int | 1134 | int |
1129 | CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, | 1135 | CIFSSMBWrite2(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 |
184 | static int | 187 | static int |
185 | smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, | 188 | smb_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 | ||
261 | int | 275 | int |
262 | SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, | 276 | SendReceive2(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); |