Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'v6.11-rc5-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

- copy_file_range fix

- two read fixes including read past end of file rc fix and read retry
crediting fix

- falloc zero range fix

* tag 'v6.11-rc5-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
cifs: Fix FALLOC_FL_ZERO_RANGE to preflush buffered part of target region
cifs: Fix copy offload to flush destination region
netfs, cifs: Fix handling of short DIO read
cifs: Fix lack of credit renegotiation on read retry

+94 -45
+13 -6
fs/netfs/io.c
··· 306 306 break; 307 307 subreq->source = NETFS_DOWNLOAD_FROM_SERVER; 308 308 subreq->error = 0; 309 + __set_bit(NETFS_SREQ_RETRYING, &subreq->flags); 309 310 netfs_stat(&netfs_n_rh_download_instead); 310 311 trace_netfs_sreq(subreq, netfs_sreq_trace_download_instead); 311 312 netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit); ··· 314 313 netfs_reset_subreq_iter(rreq, subreq); 315 314 netfs_read_from_server(rreq, subreq); 316 315 } else if (test_bit(NETFS_SREQ_SHORT_IO, &subreq->flags)) { 316 + __set_bit(NETFS_SREQ_RETRYING, &subreq->flags); 317 317 netfs_reset_subreq_iter(rreq, subreq); 318 318 netfs_rreq_short_read(rreq, subreq); 319 319 } ··· 368 366 if (subreq->error || subreq->transferred == 0) 369 367 break; 370 368 transferred += subreq->transferred; 371 - if (subreq->transferred < subreq->len) 369 + if (subreq->transferred < subreq->len || 370 + test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags)) 372 371 break; 373 372 } 374 373 ··· 504 501 505 502 subreq->error = 0; 506 503 subreq->transferred += transferred_or_error; 507 - if (subreq->transferred < subreq->len) 504 + if (subreq->transferred < subreq->len && 505 + !test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags)) 508 506 goto incomplete; 509 507 510 508 complete: ··· 784 780 TASK_UNINTERRUPTIBLE); 785 781 786 782 ret = rreq->error; 787 - if (ret == 0 && rreq->submitted < rreq->len && 788 - rreq->origin != NETFS_DIO_READ) { 789 - trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read); 790 - ret = -EIO; 783 + if (ret == 0) { 784 + if (rreq->origin == NETFS_DIO_READ) { 785 + ret = rreq->transferred; 786 + } else if (rreq->submitted < rreq->len) { 787 + trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read); 788 + ret = -EIO; 789 + } 791 790 } 792 791 } else { 793 792 /* If we decrement nr_outstanding to 0, the ref belongs to us. */
+4 -17
fs/smb/client/cifsfs.c
··· 1341 1341 struct cifsFileInfo *smb_file_target; 1342 1342 struct cifs_tcon *src_tcon; 1343 1343 struct cifs_tcon *target_tcon; 1344 - unsigned long long destend, fstart, fend; 1345 1344 ssize_t rc; 1346 1345 1347 1346 cifs_dbg(FYI, "copychunk range\n"); ··· 1390 1391 goto unlock; 1391 1392 } 1392 1393 1393 - destend = destoff + len - 1; 1394 - 1395 - /* Flush the folios at either end of the destination range to prevent 1396 - * accidental loss of dirty data outside of the range. 1394 + /* Flush and invalidate all the folios in the destination region. If 1395 + * the copy was successful, then some of the flush is extra overhead, 1396 + * but we need to allow for the copy failing in some way (eg. ENOSPC). 1397 1397 */ 1398 - fstart = destoff; 1399 - fend = destend; 1400 - 1401 - rc = cifs_flush_folio(target_inode, destoff, &fstart, &fend, true); 1398 + rc = filemap_invalidate_inode(target_inode, true, destoff, destoff + len - 1); 1402 1399 if (rc) 1403 1400 goto unlock; 1404 - rc = cifs_flush_folio(target_inode, destend, &fstart, &fend, false); 1405 - if (rc) 1406 - goto unlock; 1407 - if (fend > target_cifsi->netfs.zero_point) 1408 - target_cifsi->netfs.zero_point = fend + 1; 1409 - 1410 - /* Discard all the folios that overlap the destination region. */ 1411 - truncate_inode_pages_range(&target_inode->i_data, fstart, fend); 1412 1401 1413 1402 fscache_invalidate(cifs_inode_cookie(target_inode), NULL, 1414 1403 i_size_read(target_inode), 0);
+1
fs/smb/client/cifsglob.h
··· 1485 1485 struct cifs_io_request *req; 1486 1486 }; 1487 1487 ssize_t got_bytes; 1488 + size_t actual_len; 1488 1489 unsigned int xid; 1489 1490 int result; 1490 1491 bool have_xid;
+33 -4
fs/smb/client/file.c
··· 111 111 goto fail; 112 112 } 113 113 114 + wdata->actual_len = wdata->subreq.len; 114 115 rc = adjust_credits(wdata->server, wdata, cifs_trace_rw_credits_issue_write_adjust); 115 116 if (rc) 116 117 goto fail; ··· 154 153 struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq); 155 154 struct TCP_Server_Info *server = req->server; 156 155 struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb); 157 - size_t rsize = 0; 156 + size_t rsize; 158 157 int rc; 159 158 160 159 rdata->xid = get_xid(); ··· 167 166 cifs_sb->ctx); 168 167 169 168 170 - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, &rsize, 171 - &rdata->credits); 169 + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, 170 + &rsize, &rdata->credits); 172 171 if (rc) { 173 172 subreq->error = rc; 174 173 return false; ··· 184 183 server->credits, server->in_flight, 0, 185 184 cifs_trace_rw_credits_read_submit); 186 185 187 - subreq->len = min_t(size_t, subreq->len, rsize); 186 + subreq->len = umin(subreq->len, rsize); 187 + rdata->actual_len = subreq->len; 188 188 189 189 #ifdef CONFIG_CIFS_SMB_DIRECT 190 190 if (server->smbd_conn) ··· 205 203 struct netfs_io_request *rreq = subreq->rreq; 206 204 struct cifs_io_subrequest *rdata = container_of(subreq, struct cifs_io_subrequest, subreq); 207 205 struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq); 206 + struct TCP_Server_Info *server = req->server; 207 + struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb); 208 208 int rc = 0; 209 209 210 210 cifs_dbg(FYI, "%s: op=%08x[%x] mapping=%p len=%zu/%zu\n", 211 211 __func__, rreq->debug_id, subreq->debug_index, rreq->mapping, 212 212 subreq->transferred, subreq->len); 213 + 214 + if (test_bit(NETFS_SREQ_RETRYING, &subreq->flags)) { 215 + /* 216 + * As we're issuing a retry, we need to negotiate some new 217 + * credits otherwise the server may reject the op with 218 + * INVALID_PARAMETER. Note, however, we may get back less 219 + * credit than we need to complete the op, in which case, we 220 + * shorten the op and rely on additional rounds of retry. 221 + */ 222 + size_t rsize = umin(subreq->len - subreq->transferred, 223 + cifs_sb->ctx->rsize); 224 + 225 + rc = server->ops->wait_mtu_credits(server, rsize, &rdata->actual_len, 226 + &rdata->credits); 227 + if (rc) 228 + goto out; 229 + 230 + rdata->credits.in_flight_check = 1; 231 + 232 + trace_smb3_rw_credits(rdata->rreq->debug_id, 233 + rdata->subreq.debug_index, 234 + rdata->credits.value, 235 + server->credits, server->in_flight, 0, 236 + cifs_trace_rw_credits_read_resubmit); 237 + } 213 238 214 239 if (req->cfile->invalidHandle) { 215 240 do {
+15 -3
fs/smb/client/smb2ops.c
··· 301 301 unsigned int /*enum smb3_rw_credits_trace*/ trace) 302 302 { 303 303 struct cifs_credits *credits = &subreq->credits; 304 - int new_val = DIV_ROUND_UP(subreq->subreq.len, SMB2_MAX_BUFFER_SIZE); 304 + int new_val = DIV_ROUND_UP(subreq->actual_len, SMB2_MAX_BUFFER_SIZE); 305 305 int scredits, in_flight; 306 306 307 307 if (!credits->value || credits->value == new_val) ··· 3237 3237 } 3238 3238 3239 3239 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, 3240 - loff_t offset, loff_t len, bool keep_size) 3240 + unsigned long long offset, unsigned long long len, 3241 + bool keep_size) 3241 3242 { 3242 3243 struct cifs_ses *ses = tcon->ses; 3243 3244 struct inode *inode = file_inode(file); 3244 3245 struct cifsInodeInfo *cifsi = CIFS_I(inode); 3245 3246 struct cifsFileInfo *cfile = file->private_data; 3246 - unsigned long long new_size; 3247 + struct netfs_inode *ictx = netfs_inode(inode); 3248 + unsigned long long i_size, new_size, remote_size; 3247 3249 long rc; 3248 3250 unsigned int xid; 3249 3251 ··· 3256 3254 3257 3255 inode_lock(inode); 3258 3256 filemap_invalidate_lock(inode->i_mapping); 3257 + 3258 + i_size = i_size_read(inode); 3259 + remote_size = ictx->remote_i_size; 3260 + if (offset + len >= remote_size && offset < i_size) { 3261 + unsigned long long top = umin(offset + len, i_size); 3262 + 3263 + rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1); 3264 + if (rc < 0) 3265 + goto zero_range_exit; 3266 + } 3259 3267 3260 3268 /* 3261 3269 * We zero the range through ioctl, so we need remove the page caches
+26 -15
fs/smb/client/smb2pdu.c
··· 4507 4507 smb2_readv_callback(struct mid_q_entry *mid) 4508 4508 { 4509 4509 struct cifs_io_subrequest *rdata = mid->callback_data; 4510 + struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode); 4510 4511 struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); 4511 4512 struct TCP_Server_Info *server = rdata->server; 4512 4513 struct smb2_hdr *shdr = ··· 4530 4529 "rdata server %p != mid server %p", 4531 4530 rdata->server, mid->server); 4532 4531 4533 - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu\n", 4532 + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu/%zu\n", 4534 4533 __func__, mid->mid, mid->mid_state, rdata->result, 4535 - rdata->subreq.len); 4534 + rdata->actual_len, rdata->subreq.len - rdata->subreq.transferred); 4536 4535 4537 4536 switch (mid->mid_state) { 4538 4537 case MID_RESPONSE_RECEIVED: ··· 4586 4585 rdata->subreq.debug_index, 4587 4586 rdata->xid, 4588 4587 rdata->req->cfile->fid.persistent_fid, 4589 - tcon->tid, tcon->ses->Suid, rdata->subreq.start, 4590 - rdata->subreq.len, rdata->result); 4588 + tcon->tid, tcon->ses->Suid, 4589 + rdata->subreq.start + rdata->subreq.transferred, 4590 + rdata->actual_len, 4591 + rdata->result); 4591 4592 } else 4592 4593 trace_smb3_read_done(rdata->rreq->debug_id, 4593 4594 rdata->subreq.debug_index, 4594 4595 rdata->xid, 4595 4596 rdata->req->cfile->fid.persistent_fid, 4596 4597 tcon->tid, tcon->ses->Suid, 4597 - rdata->subreq.start, rdata->got_bytes); 4598 + rdata->subreq.start + rdata->subreq.transferred, 4599 + rdata->got_bytes); 4598 4600 4599 4601 if (rdata->result == -ENODATA) { 4600 - /* We may have got an EOF error because fallocate 4601 - * failed to enlarge the file. 4602 - */ 4603 - if (rdata->subreq.start < rdata->subreq.rreq->i_size) 4602 + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); 4603 + rdata->result = 0; 4604 + } else { 4605 + if (rdata->got_bytes < rdata->actual_len && 4606 + rdata->subreq.start + rdata->subreq.transferred + rdata->got_bytes == 4607 + ictx->remote_i_size) { 4608 + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); 4604 4609 rdata->result = 0; 4610 + } 4605 4611 } 4606 4612 trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value, 4607 4613 server->credits, server->in_flight, ··· 4629 4621 { 4630 4622 int rc, flags = 0; 4631 4623 char *buf; 4624 + struct netfs_io_subrequest *subreq = &rdata->subreq; 4632 4625 struct smb2_hdr *shdr; 4633 4626 struct cifs_io_parms io_parms; 4634 4627 struct smb_rqst rqst = { .rq_iov = rdata->iov, ··· 4640 4631 int credit_request; 4641 4632 4642 4633 cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n", 4643 - __func__, rdata->subreq.start, rdata->subreq.len); 4634 + __func__, subreq->start, subreq->len); 4644 4635 4645 4636 if (!rdata->server) 4646 4637 rdata->server = cifs_pick_channel(tcon->ses); 4647 4638 4648 4639 io_parms.tcon = tlink_tcon(rdata->req->cfile->tlink); 4649 4640 io_parms.server = server = rdata->server; 4650 - io_parms.offset = rdata->subreq.start; 4651 - io_parms.length = rdata->subreq.len; 4641 + io_parms.offset = subreq->start + subreq->transferred; 4642 + io_parms.length = rdata->actual_len; 4652 4643 io_parms.persistent_fid = rdata->req->cfile->fid.persistent_fid; 4653 4644 io_parms.volatile_fid = rdata->req->cfile->fid.volatile_fid; 4654 4645 io_parms.pid = rdata->req->pid; ··· 4663 4654 4664 4655 rdata->iov[0].iov_base = buf; 4665 4656 rdata->iov[0].iov_len = total_len; 4657 + rdata->got_bytes = 0; 4658 + rdata->result = 0; 4666 4659 4667 4660 shdr = (struct smb2_hdr *)buf; 4668 4661 4669 4662 if (rdata->credits.value > 0) { 4670 - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->subreq.len, 4663 + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->actual_len, 4671 4664 SMB2_MAX_BUFFER_SIZE)); 4672 4665 credit_request = le16_to_cpu(shdr->CreditCharge) + 8; 4673 4666 if (server->credits >= server->max_credits) ··· 4693 4682 if (rc) { 4694 4683 cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); 4695 4684 trace_smb3_read_err(rdata->rreq->debug_id, 4696 - rdata->subreq.debug_index, 4685 + subreq->debug_index, 4697 4686 rdata->xid, io_parms.persistent_fid, 4698 4687 io_parms.tcon->tid, 4699 4688 io_parms.tcon->ses->Suid, 4700 - io_parms.offset, io_parms.length, rc); 4689 + io_parms.offset, rdata->actual_len, rc); 4701 4690 } 4702 4691 4703 4692 async_readv_out:
+1
fs/smb/client/trace.h
··· 30 30 EM(cifs_trace_rw_credits_old_session, "old-session") \ 31 31 EM(cifs_trace_rw_credits_read_response_add, "rd-resp-add") \ 32 32 EM(cifs_trace_rw_credits_read_response_clear, "rd-resp-clr") \ 33 + EM(cifs_trace_rw_credits_read_resubmit, "rd-resubmit") \ 33 34 EM(cifs_trace_rw_credits_read_submit, "rd-submit ") \ 34 35 EM(cifs_trace_rw_credits_write_prepare, "wr-prepare ") \ 35 36 EM(cifs_trace_rw_credits_write_response_add, "wr-resp-add") \
+1
include/linux/netfs.h
··· 198 198 #define NETFS_SREQ_NEED_RETRY 9 /* Set if the filesystem requests a retry */ 199 199 #define NETFS_SREQ_RETRYING 10 /* Set if we're retrying */ 200 200 #define NETFS_SREQ_FAILED 11 /* Set if the subreq failed unretryably */ 201 + #define NETFS_SREQ_HIT_EOF 12 /* Set if we hit the EOF */ 201 202 }; 202 203 203 204 enum netfs_io_origin {