diff options
Diffstat (limited to 'fs/cifs/smb2misc.c')
-rw-r--r-- | fs/cifs/smb2misc.c | 74 |
1 files changed, 69 insertions, 5 deletions
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 3a7f8bd5127d..cd31715f03f4 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -389,6 +389,27 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) | |||
389 | return 0; | 389 | return 0; |
390 | } | 390 | } |
391 | 391 | ||
392 | struct smb2_lease_break_work { | ||
393 | struct work_struct lease_break; | ||
394 | struct tcon_link *tlink; | ||
395 | __u8 lease_key[16]; | ||
396 | __le32 lease_state; | ||
397 | }; | ||
398 | |||
399 | static void | ||
400 | cifs_ses_oplock_break(struct work_struct *work) | ||
401 | { | ||
402 | struct smb2_lease_break_work *lw = container_of(work, | ||
403 | struct smb2_lease_break_work, lease_break); | ||
404 | int rc; | ||
405 | |||
406 | rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, | ||
407 | lw->lease_state); | ||
408 | cFYI(1, "Lease release rc %d", rc); | ||
409 | cifs_put_tlink(lw->tlink); | ||
410 | kfree(lw); | ||
411 | } | ||
412 | |||
392 | static bool | 413 | static bool |
393 | smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) | 414 | smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) |
394 | { | 415 | { |
@@ -398,6 +419,19 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) | |||
398 | struct cifs_tcon *tcon; | 419 | struct cifs_tcon *tcon; |
399 | struct cifsInodeInfo *cinode; | 420 | struct cifsInodeInfo *cinode; |
400 | struct cifsFileInfo *cfile; | 421 | struct cifsFileInfo *cfile; |
422 | struct cifs_pending_open *open; | ||
423 | struct smb2_lease_break_work *lw; | ||
424 | bool found; | ||
425 | int ack_req = rsp->Flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; | ||
426 | |||
427 | lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); | ||
428 | if (!lw) { | ||
429 | cERROR(1, "Memory allocation failed during lease break check"); | ||
430 | return false; | ||
431 | } | ||
432 | |||
433 | INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); | ||
434 | lw->lease_state = rsp->NewLeaseState; | ||
401 | 435 | ||
402 | cFYI(1, "Checking for lease break"); | 436 | cFYI(1, "Checking for lease break"); |
403 | 437 | ||
@@ -405,28 +439,29 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) | |||
405 | spin_lock(&cifs_tcp_ses_lock); | 439 | spin_lock(&cifs_tcp_ses_lock); |
406 | list_for_each(tmp, &server->smb_ses_list) { | 440 | list_for_each(tmp, &server->smb_ses_list) { |
407 | ses = list_entry(tmp, struct cifs_ses, smb_ses_list); | 441 | ses = list_entry(tmp, struct cifs_ses, smb_ses_list); |
442 | |||
443 | spin_lock(&cifs_file_list_lock); | ||
408 | list_for_each(tmp1, &ses->tcon_list) { | 444 | list_for_each(tmp1, &ses->tcon_list) { |
409 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | 445 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); |
410 | 446 | ||
411 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 447 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
412 | spin_lock(&cifs_file_list_lock); | ||
413 | list_for_each(tmp2, &tcon->openFileList) { | 448 | list_for_each(tmp2, &tcon->openFileList) { |
414 | cfile = list_entry(tmp2, struct cifsFileInfo, | 449 | cfile = list_entry(tmp2, struct cifsFileInfo, |
415 | tlist); | 450 | tlist); |
416 | cinode = CIFS_I(cfile->dentry->d_inode); | 451 | cinode = CIFS_I(cfile->dentry->d_inode); |
417 | 452 | ||
418 | if (memcmp(cinode->lease_key, rsp->LeaseKey, | 453 | if (memcmp(cinode->lease_key, rsp->LeaseKey, |
419 | SMB2_LEASE_KEY_SIZE)) | 454 | SMB2_LEASE_KEY_SIZE)) |
420 | continue; | 455 | continue; |
421 | 456 | ||
457 | cFYI(1, "found in the open list"); | ||
422 | cFYI(1, "lease key match, lease break 0x%d", | 458 | cFYI(1, "lease key match, lease break 0x%d", |
423 | le32_to_cpu(rsp->NewLeaseState)); | 459 | le32_to_cpu(rsp->NewLeaseState)); |
424 | 460 | ||
425 | smb2_set_oplock_level(cinode, | 461 | smb2_set_oplock_level(cinode, |
426 | smb2_map_lease_to_oplock(rsp->NewLeaseState)); | 462 | smb2_map_lease_to_oplock(rsp->NewLeaseState)); |
427 | 463 | ||
428 | if (rsp->Flags & | 464 | if (ack_req) |
429 | SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) | ||
430 | cfile->oplock_break_cancelled = false; | 465 | cfile->oplock_break_cancelled = false; |
431 | else | 466 | else |
432 | cfile->oplock_break_cancelled = true; | 467 | cfile->oplock_break_cancelled = true; |
@@ -437,10 +472,39 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) | |||
437 | spin_unlock(&cifs_tcp_ses_lock); | 472 | spin_unlock(&cifs_tcp_ses_lock); |
438 | return true; | 473 | return true; |
439 | } | 474 | } |
440 | spin_unlock(&cifs_file_list_lock); | 475 | |
476 | found = false; | ||
477 | list_for_each_entry(open, &tcon->pending_opens, olist) { | ||
478 | if (memcmp(open->lease_key, rsp->LeaseKey, | ||
479 | SMB2_LEASE_KEY_SIZE)) | ||
480 | continue; | ||
481 | |||
482 | if (!found && ack_req) { | ||
483 | found = true; | ||
484 | memcpy(lw->lease_key, open->lease_key, | ||
485 | SMB2_LEASE_KEY_SIZE); | ||
486 | lw->tlink = cifs_get_tlink(open->tlink); | ||
487 | queue_work(cifsiod_wq, | ||
488 | &lw->lease_break); | ||
489 | } | ||
490 | |||
491 | cFYI(1, "found in the pending open list"); | ||
492 | cFYI(1, "lease key match, lease break 0x%d", | ||
493 | le32_to_cpu(rsp->NewLeaseState)); | ||
494 | |||
495 | open->oplock = | ||
496 | smb2_map_lease_to_oplock(rsp->NewLeaseState); | ||
497 | } | ||
498 | if (found) { | ||
499 | spin_unlock(&cifs_file_list_lock); | ||
500 | spin_unlock(&cifs_tcp_ses_lock); | ||
501 | return true; | ||
502 | } | ||
441 | } | 503 | } |
504 | spin_unlock(&cifs_file_list_lock); | ||
442 | } | 505 | } |
443 | spin_unlock(&cifs_tcp_ses_lock); | 506 | spin_unlock(&cifs_tcp_ses_lock); |
507 | kfree(lw); | ||
444 | cFYI(1, "Can not process lease break - no lease matched"); | 508 | cFYI(1, "Can not process lease break - no lease matched"); |
445 | return false; | 509 | return false; |
446 | } | 510 | } |