summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McDonald <danmcd@mnx.io>2022-12-13 23:42:04 -0500
committerDan McDonald <danmcd@mnx.io>2022-12-13 23:42:04 -0500
commit3b01c8dd53b81ad475bf8152609c8c4803a9af55 (patch)
treeca054b176498eba1507a625b3ad2fe770704fd9c
parent257115fb1f00e1028920a11ada9cc1c800eead68 (diff)
parent7f6a299e282ed51917878b84744774a6634e5dc6 (diff)
downloadillumos-joyent-release-20221215.tar.gz
[illumos-gate merge]release-20221215
commit 7f6a299e282ed51917878b84744774a6634e5dc6 15210 smbtorture lease and oplock failures commit 72b35b0568511bf35ca88532dff910dc0f16847f 15188 smb oplock blocking access to folders
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case01.ref9
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case02.ref9
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case03.ref6
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case04.ref6
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case05.ref33
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case06.ref23
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case07.ref6
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case08.ref15
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case09.ref10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case10.ref10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case11.ref11
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case12.ref34
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case12.txt7
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case13.ref24
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case13.txt2
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case14.ref66
-rw-r--r--usr/src/cmd/smbsrv/testoplock/case14.txt51
-rw-r--r--usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h23
-rw-r--r--usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h10
-rw-r--r--usr/src/cmd/smbsrv/testoplock/tol_main.c139
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_dispatch.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_durable.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lease.c634
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_oplock.c286
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_cmn_oplock.c154
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_locking_andx.c74
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c33
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_oplock.c228
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c551
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h25
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h16
31 files changed, 1753 insertions, 749 deletions
diff --git a/usr/src/cmd/smbsrv/testoplock/case01.ref b/usr/src/cmd/smbsrv/testoplock/case01.ref
index 6327c46b7e..f7bf8e0773 100644
--- a/usr/src/cmd/smbsrv/testoplock/case01.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case01.ref
@@ -6,15 +6,16 @@ show
ol_state=0x410 ( BATCH_OPLOCK EXCLUSIVE )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x400 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease= State=0x400 Excl=Y onlist:
brk-open 2
*smb_oplock_ind_break fid=1 NewLevel=0x100, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x100, OldLevel=0x400, AckReq=1)
brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
show
ol_state=0x100410 ( BREAK_TO_TWO BATCH_OPLOCK EXCLUSIVE )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x400 Brk=0x100000 Excl=Y onlist:
+ fid=1 Lease= State=0x400 BreakTo=0x100 Excl=Y onlist:
ack 1
ack: break fid=1, newstate=0x100, status=0x0 (SUCCESS)
open 2
@@ -25,5 +26,5 @@ show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=2 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
- fid=2 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=1 Lease= State=0x100 Excl=N onlist: II
+ fid=2 Lease= State=0x100 Excl=N onlist: II
diff --git a/usr/src/cmd/smbsrv/testoplock/case02.ref b/usr/src/cmd/smbsrv/testoplock/case02.ref
index 73488cbf27..ef251aafd5 100644
--- a/usr/src/cmd/smbsrv/testoplock/case02.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case02.ref
@@ -6,15 +6,16 @@ show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease= State=0x807 Excl=Y onlist:
brk-open 2
*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1)
brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
show
ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x807 Brk=0x30000 Excl=Y onlist:
+ fid=1 Lease= State=0x807 BreakTo=0x803 Excl=Y onlist:
ack 1
ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
open 2
@@ -25,5 +26,5 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=2 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=2 Lease= OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=1 Lease= State=0x803 Excl=N onlist: RH
+ fid=2 Lease= State=0x803 Excl=N onlist: RH
diff --git a/usr/src/cmd/smbsrv/testoplock/case03.ref b/usr/src/cmd/smbsrv/testoplock/case03.ref
index cdc7fe0089..0549760a2c 100644
--- a/usr/src/cmd/smbsrv/testoplock/case03.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case03.ref
@@ -6,7 +6,7 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=1 Lease=3 State=0x803 Excl=N onlist: RH
open 2 3
open 2 OK
req 2 0x803
@@ -16,5 +16,5 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=2 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=1 Lease=3 State=0x0 Excl=N onlist:
+ fid=2 Lease=3 State=0x803 Excl=N onlist: RH
diff --git a/usr/src/cmd/smbsrv/testoplock/case04.ref b/usr/src/cmd/smbsrv/testoplock/case04.ref
index 32df3767a9..c7eed6cafb 100644
--- a/usr/src/cmd/smbsrv/testoplock/case04.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case04.ref
@@ -6,7 +6,7 @@ show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=3 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=3 State=0x801 Excl=N onlist: R
open 2 3
open 2 OK
req 2 0x801
@@ -16,5 +16,5 @@ show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=2 Lease=3 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=3 State=0x0 Excl=N onlist:
+ fid=2 Lease=3 State=0x801 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case05.ref b/usr/src/cmd/smbsrv/testoplock/case05.ref
index 639bf0023b..cad3575fa8 100644
--- a/usr/src/cmd/smbsrv/testoplock/case05.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case05.ref
@@ -6,7 +6,7 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=1
- fid=2 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease=4 State=0x803 Excl=N onlist: RH
open 3 4
open 3 OK
req 3 0x803
@@ -16,29 +16,30 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease=4 State=0x0 Excl=N onlist:
+ fid=3 Lease=4 State=0x803 Excl=N onlist: RH
open 1
open 1 OK
brk-write 1
*smb_oplock_ind_break fid=3 NewLevel=0x0, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=3 NewLevel=0x800, OldLevel=0x803, AckReq=1)
brk-write 1 ret status=0x0 (SUCCESS)
show
ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1
ofile_cnt=3
- fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease=4 OgState=0x803 Brk=0x80000 Excl=N onlist: RHBQ(to none)
- fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=4 State=0x0 Excl=N onlist:
+ fid=3 Lease=4 State=0x803 BreakTo=0x800 Excl=N onlist: RHBQ(to none)
+ fid=1 Lease= State=0x0 Excl=N onlist:
ack 3
ack: break fid=3, newstate=0x800, status=0x0 (SUCCESS)
show
ol_state=0x10000000 ( NO_OPLOCK )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=3
- fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease=4 OgState=0x800 Brk=0x0 Excl=N onlist:
- fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=4 State=0x0 Excl=N onlist:
+ fid=3 Lease=4 State=0x800 Excl=N onlist:
+ fid=1 Lease= State=0x0 Excl=N onlist:
open 4 4
open 4 OK
req 4 0x803
@@ -47,10 +48,10 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=4
- fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease=4 OgState=0x800 Brk=0x0 Excl=N onlist:
- fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=4 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=2 Lease=4 State=0x0 Excl=N onlist:
+ fid=3 Lease=4 State=0x800 Excl=N onlist:
+ fid=1 Lease= State=0x0 Excl=N onlist:
+ fid=4 Lease=4 State=0x803 Excl=N onlist: RH
close 4
*smb_oplock_ind_break fid=4 NewLevel=0x0, AckReq=0, ComplStatus=0x216 (OPLOCK_HANDLE_CLOSED)
close OK
@@ -60,6 +61,6 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=3
- fid=2 Lease=4 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease=4 OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=1 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=2 Lease=4 State=0x0 Excl=N onlist:
+ fid=3 Lease=4 State=0x803 Excl=N onlist: RH
+ fid=1 Lease= State=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case06.ref b/usr/src/cmd/smbsrv/testoplock/case06.ref
index daa69c3dd1..15a588219d 100644
--- a/usr/src/cmd/smbsrv/testoplock/case06.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case06.ref
@@ -10,17 +10,18 @@ show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
- fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
brk-write 1
*smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=2 NewLevel=0x800, OldLevel=0x801, AckReq=0)
brk-write 1 ret status=0x0 (SUCCESS)
show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
- fid=2 Lease=2 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
+ fid=2 Lease=2 State=0x800 Excl=N onlist:
open 3 2
open 3 OK
req 3 0x801
@@ -34,23 +35,25 @@ show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
- fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
brk-write 2
*smb_oplock_ind_break fid=1 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x800, OldLevel=0x801, AckReq=0)
brk-write 2 ret status=0x0 (SUCCESS)
show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
- fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x800 Excl=N onlist:
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
brk-write 1
*smb_oplock_ind_break fid=2 NewLevel=0x0, AckReq=0, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=2 NewLevel=0x800, OldLevel=0x801, AckReq=0)
brk-write 1 ret status=0x0 (SUCCESS)
show
ol_state=0x10000000 ( NO_OPLOCK )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
- fid=2 Lease=2 OgState=0x800 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x800 Excl=N onlist:
+ fid=2 Lease=2 State=0x800 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case07.ref b/usr/src/cmd/smbsrv/testoplock/case07.ref
index 11b1fa415d..0427292339 100644
--- a/usr/src/cmd/smbsrv/testoplock/case07.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case07.ref
@@ -6,7 +6,7 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=3 OgState=0x803 Brk=0x0 Excl=N onlist: RH
+ fid=1 Lease=3 State=0x803 Excl=N onlist: RH
open 2 3
open 2 OK
req 2 0x807
@@ -16,5 +16,5 @@ show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=3 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=2 Lease=3 OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=3 State=0x0 Excl=N onlist:
+ fid=2 Lease=3 State=0x807 Excl=Y onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case08.ref b/usr/src/cmd/smbsrv/testoplock/case08.ref
index 7f28032990..1bc366958d 100644
--- a/usr/src/cmd/smbsrv/testoplock/case08.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case08.ref
@@ -6,31 +6,32 @@ show
ol_state=0x15 ( EXCLUSIVE WRITE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=1 OgState=0x805 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=1 State=0x805 Excl=Y onlist:
open 2 2
open 2 OK
brk-open 2
*smb_oplock_ind_break fid=1 NewLevel=0x1, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x801, OldLevel=0x805, AckReq=1)
brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
show
ol_state=0x10015 ( BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x805 Brk=0x10000 Excl=Y onlist:
- fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x805 BreakTo=0x801 Excl=Y onlist:
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
ack 1
ack: break fid=1, newstate=0x801, status=0x0 (SUCCESS)
show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
- fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
req 2 0x801
req oplock fid=2 ret oplock=0x801 status=0x0 (SUCCESS)
show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=2 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
- fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case09.ref b/usr/src/cmd/smbsrv/testoplock/case09.ref
index 4003b133db..9eb3e85230 100644
--- a/usr/src/cmd/smbsrv/testoplock/case09.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case09.ref
@@ -6,7 +6,7 @@ show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=1 Lease= State=0x100 Excl=N onlist: II
open 2 2
open 2 OK
brk-open 2
@@ -15,13 +15,13 @@ show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
- fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease= State=0x100 Excl=N onlist: II
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
req 2 0x803
req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
- fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease= State=0x100 Excl=N onlist: II
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case10.ref b/usr/src/cmd/smbsrv/testoplock/case10.ref
index 9e0275d258..c3e4fdae96 100644
--- a/usr/src/cmd/smbsrv/testoplock/case10.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case10.ref
@@ -6,7 +6,7 @@ show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
+ fid=1 Lease= State=0x100 Excl=N onlist: II
open 2 2
open 2 OK
brk-open 2
@@ -15,8 +15,8 @@ show
ol_state=0x100 ( LEVEL_TWO_OPLOCK )
Excl=n cnt_II=1 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
- fid=2 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease= State=0x100 Excl=N onlist: II
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
req 2 0x807
req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
req 2 0x803
@@ -27,5 +27,5 @@ show
ol_state=0x101 ( LEVEL_TWO_OPLOCK READ_CACHING )
Excl=n cnt_II=1 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease= OgState=0x100 Brk=0x0 Excl=N onlist: II
- fid=2 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease= State=0x100 Excl=N onlist: II
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case11.ref b/usr/src/cmd/smbsrv/testoplock/case11.ref
index 05bae87e04..d1a88b4518 100644
--- a/usr/src/cmd/smbsrv/testoplock/case11.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case11.ref
@@ -6,11 +6,12 @@ show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
open 2
open 2 OK
brk-open 2
*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1)
brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
ack 1
ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
@@ -18,13 +19,13 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=2 Lease= State=0x0 Excl=N onlist:
req 2 0x100
req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=2 Lease= State=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case12.ref b/usr/src/cmd/smbsrv/testoplock/case12.ref
index bc36b7b6de..ebbe5d4c18 100644
--- a/usr/src/cmd/smbsrv/testoplock/case12.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case12.ref
@@ -6,11 +6,12 @@ show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
open 2
open 2 OK
brk-open 2
*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1)
brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
waiters 2 1
waiters 0 -> 1
@@ -18,8 +19,8 @@ show
ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x807 Brk=0x30000 Excl=Y onlist:
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist:
+ fid=2 Lease= State=0x0 Excl=N onlist:
open 3
open 3 OK
brk-open 3 4
@@ -30,25 +31,28 @@ show
ol_state=0x80017 ( BREAK_TO_NO_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=3
- fid=1 Lease=1 OgState=0x807 Brk=0x30000 Excl=Y onlist:
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist:
+ fid=2 Lease= State=0x0 Excl=N onlist:
+ fid=3 Lease= State=0x0 Excl=N onlist:
ack 1 0x803
-*smb_oplock_ind_break fid=1 NewLevel=0x0, AckReq=1, ComplStatus=0x8000002e (CANNOT_GRANT_REQUESTED_OPLOCK)
ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+*smb_oplock_ind_break_in_ack fid=1 NewLevel=0x1, AckReq=1
+*smb_oplock_send_break fid=1 NewLevel=0x801, OldLevel=0x803, AckReq=1)
show
ol_state=0x80003 ( BREAK_TO_NO_CACHING HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=1
ofile_cnt=3
- fid=1 Lease=1 OgState=0x803 Brk=0x80000 Excl=N onlist: RHBQ(to none)
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
-ack 1 0x800
- ack: break fid=1, newstate=0x800, status=0x0 (SUCCESS)
+ fid=1 Lease=1 State=0x803 BreakTo=0x801 Excl=N onlist: RHBQ(to none)
+ fid=2 Lease= State=0x0 Excl=N onlist:
+ fid=3 Lease= State=0x0 Excl=N onlist:
+ack 1 0x801
+ ack: break fid=1, newstate=0x801, status=0x0 (SUCCESS)
+*smb_oplock_ind_break_in_ack fid=1 NewLevel=0x0, AckReq=0
+*smb_oplock_send_break fid=1 NewLevel=0x800, OldLevel=0x801, AckReq=0)
show
ol_state=0x10000000 ( NO_OPLOCK )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=3
- fid=1 Lease=1 OgState=0x800 Brk=0x0 Excl=N onlist:
- fid=2 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=3 Lease= OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x800 Excl=N onlist:
+ fid=2 Lease= State=0x0 Excl=N onlist:
+ fid=3 Lease= State=0x0 Excl=N onlist:
diff --git a/usr/src/cmd/smbsrv/testoplock/case12.txt b/usr/src/cmd/smbsrv/testoplock/case12.txt
index 23b4f63092..1cbaed4d72 100644
--- a/usr/src/cmd/smbsrv/testoplock/case12.txt
+++ b/usr/src/cmd/smbsrv/testoplock/case12.txt
@@ -20,9 +20,10 @@ show
# and brk-open shoud block (break in progress)
#
# ack the first lease break above (RWH to RH)
-# should get a new break ind. (RH to none)
+# should get a new break ind. (RH to R) ar=1
ack 1 0x803
show
-# got break ind?
-ack 1 0x800
+# ack the second lease break (RH to R)
+# should get a new break ind. (R to none) ar=0
+ack 1 0x801
show
diff --git a/usr/src/cmd/smbsrv/testoplock/case13.ref b/usr/src/cmd/smbsrv/testoplock/case13.ref
index a35a5992ce..9dc00b5ce6 100644
--- a/usr/src/cmd/smbsrv/testoplock/case13.ref
+++ b/usr/src/cmd/smbsrv/testoplock/case13.ref
@@ -6,7 +6,7 @@ show
ol_state=0x1 ( READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=1 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x801 Excl=N onlist: R
open 2 1
open 2 OK
@@ -19,23 +19,31 @@ show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=2) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x0 Brk=0x0 Excl=N onlist:
- fid=2 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=1 State=0x0 Excl=N onlist:
+ fid=2 Lease=1 State=0x807 Excl=Y onlist:
move 2 1
move 2 1
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
+ fid=2 Lease=1 State=0x0 Excl=N onlist:
+
close 2
close OK
show
ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
ofile_cnt=1
- fid=1 Lease=1 OgState=0x807 Brk=0x0 Excl=Y onlist:
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
open 3 2
open 3 OK
brk-open 3
*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1)
brk-open 3 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
ack 1
ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
@@ -43,8 +51,8 @@ show
ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=3 Lease=2 OgState=0x0 Brk=0x0 Excl=N onlist:
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=3 Lease=2 State=0x0 Excl=N onlist:
req 3 0x801
req oplock fid=3 ret oplock=0x801 status=0x0 (SUCCESS)
@@ -52,5 +60,5 @@ show
ol_state=0x23 ( MIXED_R_AND_RH HANDLE_CACHING READ_CACHING )
Excl=n cnt_II=0 cnt_R=1 cnt_RH=1 cnt_RHBQ=0
ofile_cnt=2
- fid=1 Lease=1 OgState=0x803 Brk=0x0 Excl=N onlist: RH
- fid=3 Lease=2 OgState=0x801 Brk=0x0 Excl=N onlist: R
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=3 Lease=2 State=0x801 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case13.txt b/usr/src/cmd/smbsrv/testoplock/case13.txt
index c8d012690d..f7af15d5c9 100644
--- a/usr/src/cmd/smbsrv/testoplock/case13.txt
+++ b/usr/src/cmd/smbsrv/testoplock/case13.txt
@@ -12,6 +12,8 @@ req 2 0x807
show
move 2 1
+show
+
close 2
show
diff --git a/usr/src/cmd/smbsrv/testoplock/case14.ref b/usr/src/cmd/smbsrv/testoplock/case14.ref
new file mode 100644
index 0000000000..b019488ae9
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case14.ref
@@ -0,0 +1,66 @@
+
+open 1 1
+ open 1 OK
+req 1 0x807
+ req oplock fid=1 ret oplock=0x807 status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
+
+brk-setinfo 1 0xa
+ brk-setinfo 1 0xa ret status=0x0 (SUCCESS)
+show
+ ol_state=0x17 ( EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=1
+ fid=1 Lease=1 State=0x807 Excl=Y onlist:
+
+
+open 2 2
+ open 2 OK
+brk-open 2
+*smb_oplock_ind_break fid=1 NewLevel=0x3, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=1 NewLevel=0x803, OldLevel=0x807, AckReq=1)
+ brk-open 2 ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+show
+ ol_state=0x30017 ( BREAK_TO_HANDLE_CACHING BREAK_TO_READ_CACHING EXCLUSIVE WRITE_CACHING HANDLE_CACHING READ_CACHING )
+ Excl=Y (FID=1) cnt_II=0 cnt_R=0 cnt_RH=0 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 State=0x807 BreakTo=0x803 Excl=Y onlist:
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
+
+ack 1 0x803
+ ack: break fid=1, newstate=0x803, status=0x0 (SUCCESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=2 Lease=2 State=0x0 Excl=N onlist:
+
+req 2 0x807
+ req oplock fid=2 ret oplock=0x0 status=0xc00000e2 (OPLOCK_NOT_GRANTED)
+req 2 0x803
+ req oplock fid=2 ret oplock=0x803 status=0x0 (SUCCESS)
+
+brk-setinfo 1 0xa
+*smb_oplock_ind_break fid=2 NewLevel=0x1, AckReq=1, ComplStatus=0x0 (SUCCESS)
+*smb_oplock_send_break fid=2 NewLevel=0x801, OldLevel=0x803, AckReq=1)
+ brk-setinfo 1 0xa ret status=0x108 (OPLOCK_BREAK_IN_PROGRESS)
+show
+ ol_state=0x3 ( HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=0 cnt_RH=1 cnt_RHBQ=1
+ ofile_cnt=2
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=2 Lease=2 State=0x803 BreakTo=0x801 Excl=N onlist: RHBQ(to read)
+
+ack 2 0x801
+ ack: break fid=2, newstate=0x801, status=0x0 (SUCCESS)
+show
+ ol_state=0x23 ( MIXED_R_AND_RH HANDLE_CACHING READ_CACHING )
+ Excl=n cnt_II=0 cnt_R=1 cnt_RH=1 cnt_RHBQ=0
+ ofile_cnt=2
+ fid=1 Lease=1 State=0x803 Excl=N onlist: RH
+ fid=2 Lease=2 State=0x801 Excl=N onlist: R
diff --git a/usr/src/cmd/smbsrv/testoplock/case14.txt b/usr/src/cmd/smbsrv/testoplock/case14.txt
new file mode 100644
index 0000000000..685d4d3637
--- /dev/null
+++ b/usr/src/cmd/smbsrv/testoplock/case14.txt
@@ -0,0 +1,51 @@
+# Input for testoplock, case 13
+# simulate smbtorture smb2.lease.v2_rename
+
+# Create (open_if, lease=e0dd(1), RWH, epoch=0x4711 )
+# resp: fid=1, RWH, epoch=0x4712
+# Expect no breaks, one handle, state=RWH
+open 1 1
+req 1 0x807
+show
+
+# SetInfo fid=1, newname=...dst.dat
+# resp OK (no breaks)
+brk-setinfo 1 0xa
+show
+
+# Create, open_if, lease=e0dd(1), RWH, epoch=0x4712
+# resp fid=5 RWH, flags=0 (not breaking)
+# Close fid=5 / resp
+# This is handled without calling the common oplock layer,
+# by logic at the top of smb2_lease_acquire
+
+# Create, open_if, lease=feed(2), RWH, epoch=0x0044
+# (resp pending, will break RWH to RH)
+open 2 2
+brk-open 2
+show
+
+# Lease Break Notify, lease=e0dd(1), RWH to RH, epoch=0x4713
+# Lease Break Ack, lease=e0dd, RH
+# resp (same)
+ack 1 0x803
+show
+
+# Create-resp, fid=9 lease=feed(2), state=RH, epoch=0x0045
+# Now that open 2 breaking is done, it can request.
+# Will fail RWH, succeed RH
+req 2 0x807
+req 2 0x803
+
+# SetInfo fid=1, newname=...src.dat
+# (resp blocked -- win10 does not go pending? we do)
+# Should get rid of handle caching (RH to R)
+brk-setinfo 1 0xa
+show
+
+# Lease Break Notify, lease=feed(2), RH to R, epoch=0x0046
+# Lease Break Ack, lease=feed(2), state=R
+# Break Ack Resp (same)
+ack 2 0x801
+show
+# SetInfo resp, fid=1 (note: races with Break Ack resp.)
diff --git a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h
index f743dcd179..2a7c31235a 100644
--- a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h
+++ b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_kproto.h
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -70,6 +71,7 @@ int smb_account_connected(smb_user_t *user);
* Common oplock functions
*/
uint32_t smb_oplock_request(smb_request_t *, smb_ofile_t *, uint32_t *);
+uint32_t smb_oplock_request_LH(smb_request_t *, smb_ofile_t *, uint32_t *);
uint32_t smb_oplock_ack_break(smb_request_t *, smb_ofile_t *, uint32_t *);
uint32_t smb_oplock_break_PARENT(smb_node_t *, smb_ofile_t *);
uint32_t smb_oplock_break_OPEN(smb_node_t *, smb_ofile_t *,
@@ -83,22 +85,23 @@ uint32_t smb_oplock_break_WRITE(smb_node_t *, smb_ofile_t *);
uint32_t smb_oplock_break_SETINFO(smb_node_t *,
smb_ofile_t *ofile, uint32_t InfoClass);
uint32_t smb_oplock_break_DELETE(smb_node_t *, smb_ofile_t *);
+void smb_oplock_close(smb_ofile_t *);
+
+void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t);
+void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *,
+ uint32_t, boolean_t);
+void smb_oplock_send_break(smb_request_t *);
+
+uint32_t smb_oplock_wait_ack(smb_request_t *, uint32_t);
+uint32_t smb_oplock_wait_break(smb_request_t *, smb_node_t *, int);
+uint32_t smb_oplock_wait_break_fem(smb_node_t *, int);
void smb_oplock_move(smb_node_t *, smb_ofile_t *, smb_ofile_t *);
/*
* Protocol-specific oplock functions
- * (and "server-level" functions)
+ * (not needed here)
*/
-void smb1_oplock_acquire(smb_request_t *, boolean_t);
-void smb1_oplock_break_notification(smb_request_t *, uint32_t);
-void smb2_oplock_break_notification(smb_request_t *, uint32_t);
-void smb2_lease_break_notification(smb_request_t *, uint32_t, boolean_t);
-void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t);
-void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *,
- uint32_t, boolean_t);
-void smb_oplock_send_brk(smb_request_t *);
-uint32_t smb_oplock_wait_break(smb_node_t *, int);
int smb_lock_range_access(smb_request_t *, smb_node_t *,
uint64_t, uint64_t, boolean_t);
diff --git a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h
index 416c7b2ba4..95eab0f824 100644
--- a/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h
+++ b/usr/src/cmd/smbsrv/testoplock/smbsrv/smb_ktypes.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
- * Copyright 2021 RackTop Systems, Inc.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -96,9 +96,11 @@ typedef struct smb_oplock {
*/
typedef struct smb_oplock_grant {
/* smb protocol-level state */
- uint32_t og_state; /* latest sent to client */
- uint32_t og_breaking; /* BREAK_TO... flags */
+ uint32_t og_state; /* what client has now */
+ uint32_t og_breakto; /* level breaking to */
+ boolean_t og_breaking;
uint16_t og_dialect; /* how to send breaks */
+ kcondvar_t og_ack_cv; /* Wait for ACK */
/* File-system level state */
uint8_t onlist_II;
uint8_t onlist_R;
@@ -109,6 +111,8 @@ typedef struct smb_oplock_grant {
#define SMB_LEASE_KEY_SZ 16
+struct smb_lease;
+
#define SMB_NODE_MAGIC 0x4E4F4445 /* 'NODE' */
#define SMB_NODE_VALID(p) ASSERT((p)->n_magic == SMB_NODE_MAGIC)
diff --git a/usr/src/cmd/smbsrv/testoplock/tol_main.c b/usr/src/cmd/smbsrv/testoplock/tol_main.c
index f78729a9d6..8cc81a4343 100644
--- a/usr/src/cmd/smbsrv/testoplock/tol_main.c
+++ b/usr/src/cmd/smbsrv/testoplock/tol_main.c
@@ -36,6 +36,8 @@
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_oplock.h>
+extern const char *xlate_nt_status(uint32_t);
+
#define OPLOCK_CACHE_RWH (READ_CACHING | HANDLE_CACHING | WRITE_CACHING)
#define OPLOCK_TYPE (LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\
BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR)
@@ -48,7 +50,7 @@ smb_request_t test_sr;
uint32_t last_ind_break_level;
char cmdbuf[100];
-extern const char *xlate_nt_status(uint32_t);
+static void run_ind_break_in_ack(smb_ofile_t *);
#define BIT_DEF(name) { name, #name }
@@ -134,16 +136,20 @@ do_show(void)
printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count);
FOREACH_NODE_OFILE(node, f) {
smb_oplock_grant_t *og = &f->f_oplock;
- printf(" fid=%d Lease=%s OgState=0x%x Brk=0x%x",
+ printf(" fid=%d Lease=%s State=0x%x",
f->f_fid,
f->TargetOplockKey, /* lease */
- f->f_oplock.og_state,
- f->f_oplock.og_breaking);
- printf(" Excl=%s onlist: %s %s %s",
- (ol->excl_open == f) ? "Y" : "N",
- og->onlist_II ? "II" : "",
- og->onlist_R ? "R" : "",
- og->onlist_RH ? "RH" : "");
+ og->og_state);
+ if (og->og_breaking)
+ printf(" BreakTo=0x%x", og->og_breakto);
+ printf(" Excl=%s onlist:",
+ (ol->excl_open == f) ? "Y" : "N");
+ if (og->onlist_II)
+ printf(" II");
+ if (og->onlist_R)
+ printf(" R");
+ if (og->onlist_RH)
+ printf(" RH");
if (og->onlist_RHBQ) {
printf(" RHBQ(to %s)",
og->BreakingToRead ?
@@ -224,8 +230,13 @@ do_req(int fid, char *arg2)
* Request an oplock
*/
status = smb_oplock_request(&test_sr, ofile, &oplock);
- if (status == 0)
+ if (status == 0 ||
+ status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
ofile->f_oplock.og_state = oplock;
+ /* When no break pending, breakto=state */
+ ofile->f_oplock.og_breakto = oplock;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ }
printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n",
fid, oplock, status, xlate_nt_status(status));
}
@@ -234,6 +245,7 @@ do_req(int fid, char *arg2)
static void
do_ack(int fid, char *arg2)
{
+ smb_node_t *node = &test_node;
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t oplock;
uint32_t status;
@@ -243,17 +255,27 @@ do_ack(int fid, char *arg2)
if (arg2 != NULL)
oplock = strtol(arg2, NULL, 16);
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
ofile->f_oplock.og_breaking = 0;
status = smb_oplock_ack_break(&test_sr, ofile, &oplock);
+ if (status == 0)
+ ofile->f_oplock.og_state = oplock;
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ /* should not get this status */
printf(" ack: break fid=%d, break-in-progress\n", fid);
- ofile->f_oplock.og_state = oplock;
+ ASSERT(0);
}
- if (status == 0)
- ofile->f_oplock.og_state = oplock;
printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n",
fid, oplock, status, xlate_nt_status(status));
+
+ run_ind_break_in_ack(ofile);
}
static void
@@ -328,8 +350,8 @@ do_brk_setinfo(int fid, char *arg2)
status = smb_oplock_break_SETINFO(
&test_node, ofile, infoclass);
- printf(" brk-setinfo %d ret status=0x%x (%s)\n",
- fid, status, xlate_nt_status(status));
+ printf(" brk-setinfo %d 0x%x ret status=0x%x (%s)\n",
+ fid, infoclass, status, xlate_nt_status(status));
}
@@ -550,45 +572,41 @@ smb_lock_range_access(
}
/*
- * Test code replacement for: smb_oplock_send_brk()
+ * Test code replacement for combination of:
+ * smb_oplock_hdl_update()
+ * smb_oplock_send_break()
+ *
+ * In a real server, we would send a break to the client,
+ * and keep track (at the SMB level) whether this oplock
+ * was obtained via a lease or an old-style oplock.
*/
static void
-test_oplock_send_brk(smb_ofile_t *ofile,
+test_oplock_send_break(smb_ofile_t *ofile,
uint32_t NewLevel, boolean_t AckReq)
{
smb_oplock_grant_t *og = &ofile->f_oplock;
+ uint32_t OldLevel;
/* Skip building a message. */
if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0)
NewLevel |= OPLOCK_LEVEL_GRANULAR;
- /*
- * In a real server, we would send a break to the client,
- * and keep track (at the SMB level) whether this oplock
- * was obtained via a lease or an old-style oplock.
- */
- if (AckReq) {
- uint32_t BreakTo;
-
- if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0) {
-
- BreakTo = (NewLevel & CACHE_RWH) << BREAK_SHIFT;
- if (BreakTo == 0)
- BreakTo = BREAK_TO_NO_CACHING;
- } else {
- if ((NewLevel & LEVEL_TWO_OPLOCK) != 0)
- BreakTo = BREAK_TO_TWO;
- else
- BreakTo = BREAK_TO_NONE;
- }
- og->og_breaking = BreakTo;
- last_ind_break_level = NewLevel;
- /* Set og_state in do_ack */
- } else {
+ OldLevel = og->og_state;
+ og->og_breakto = NewLevel;
+ og->og_breaking = B_TRUE;
+
+ printf("*smb_oplock_send_break fid=%d "
+ "NewLevel=0x%x, OldLevel=0x%x, AckReq=%d)\n",
+ ofile->f_fid, NewLevel, OldLevel, AckReq);
+
+ if (!AckReq) {
og->og_state = NewLevel;
- /* Clear og_breaking in do_ack */
+ og->og_breaking = B_FALSE;
}
+
+ /* Next, smb_oplock_send_break() would send a break. */
+ last_ind_break_level = NewLevel;
}
/*
@@ -614,31 +632,62 @@ smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
case NT_STATUS_SUCCESS:
case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK:
- test_oplock_send_brk(ofile, NewLevel, AckReq);
+ test_oplock_send_break(ofile, NewLevel, AckReq);
break;
case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE:
case NT_STATUS_OPLOCK_HANDLE_CLOSED:
og->og_state = OPLOCK_LEVEL_NONE;
+ og->og_breakto = OPLOCK_LEVEL_NONE;
+ og->og_breaking = B_FALSE;
break;
default:
- /* Checked by caller. */
ASSERT(0);
break;
}
}
+/* Arrange for break_in_ack to run after ack completes. */
+static uint32_t break_in_ack_NewLevel;
+static boolean_t break_in_ack_AckReq;
+static boolean_t break_in_ack_called;
+
void
smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile,
uint32_t NewLevel, boolean_t AckRequired)
{
ASSERT(sr == &test_sr);
- smb_oplock_ind_break(ofile, NewLevel, AckRequired, STATUS_CANT_GRANT);
+
+ /* Process these after ack */
+ ASSERT(!break_in_ack_called);
+ break_in_ack_called = B_TRUE;
+ break_in_ack_NewLevel = NewLevel;
+ break_in_ack_AckReq = AckRequired;
+}
+
+static void
+run_ind_break_in_ack(smb_ofile_t *ofile)
+{
+ uint32_t NewLevel;
+ boolean_t AckReq;
+
+ /* Process these after ack */
+ if (!break_in_ack_called)
+ return;
+ break_in_ack_called = B_FALSE;
+ NewLevel = break_in_ack_NewLevel;
+ AckReq = break_in_ack_AckReq;
+
+ printf("*smb_oplock_ind_break_in_ack fid=%d NewLevel=0x%x,"
+ " AckReq=%d\n",
+ ofile->f_fid, NewLevel, AckReq);
+
+ test_oplock_send_break(ofile, NewLevel, AckReq);
}
uint32_t
-smb_oplock_wait_break(smb_node_t *node, int timeout)
+smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout)
{
printf("*smb_oplock_wait_break (state=0x%x)\n",
node->n_oplock.ol_state);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
index f01f019dec..658cf2de49 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
@@ -11,7 +11,7 @@
/*
* Copyright 2019 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2020 RackTop Systems, Inc.
+ * Copyright 2022 RackTop Systems, Inc.
*/
@@ -1815,7 +1815,7 @@ smb2sr_run_postwork(smb_request_t *top_sr)
switch (post_sr->smb2_cmd_code) {
case SMB2_OPLOCK_BREAK:
- smb_oplock_send_brk(post_sr);
+ smb_oplock_send_break(post_sr);
break;
default:
ASSERT(0);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_durable.c b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
index 190a7ca6cc..094489c658 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_durable.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
@@ -11,6 +11,7 @@
/*
* Copyright 2017-2022 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -1426,7 +1427,7 @@ smb2_dh_reconnect(smb_request_t *sr)
/*
* The ofile is now in the caller's session & tree.
*
- * In case smb_ofile_hold or smb_oplock_send_brk() are
+ * In case smb_ofile_hold or smb_oplock_send_break() are
* waiting for state RECONNECT to complete, wakeup.
*/
mutex_enter(&of->f_mutex);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lease.c b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
index 45f886e90a..c440fa16c8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_lease.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
@@ -60,10 +60,19 @@ smb2_lease_hold(smb_lease_t *ls)
mutex_exit(&ls->ls_mutex);
}
+static void
+lease_destroy(smb_lease_t *ls)
+{
+ smb_node_release(ls->ls_node);
+ mutex_destroy(&ls->ls_mutex);
+ kmem_cache_free(smb_lease_cache, ls);
+}
+
void
smb2_lease_rele(smb_lease_t *ls)
{
smb_llist_t *bucket;
+ boolean_t destroy = B_FALSE;
mutex_enter(&ls->ls_mutex);
ls->ls_refcnt--;
@@ -81,16 +90,17 @@ smb2_lease_rele(smb_lease_t *ls)
smb_llist_enter(bucket, RW_WRITER);
mutex_enter(&ls->ls_mutex);
- if (ls->ls_refcnt == 0)
- smb_llist_remove(bucket, ls);
- mutex_exit(&ls->ls_mutex);
-
if (ls->ls_refcnt == 0) {
- mutex_destroy(&ls->ls_mutex);
- kmem_cache_free(smb_lease_cache, ls);
+ smb_llist_remove(bucket, ls);
+ destroy = B_TRUE;
}
+ mutex_exit(&ls->ls_mutex);
smb_llist_exit(bucket);
+
+ if (destroy) {
+ lease_destroy(ls);
+ }
}
/*
@@ -150,6 +160,7 @@ smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
newlease->ls_bucket = bucket;
newlease->ls_node = of->f_node;
+ smb_node_ref(newlease->ls_node);
newlease->ls_refcnt = 1;
newlease->ls_epoch = op->lease_epoch;
newlease->ls_version = op->lease_version;
@@ -197,8 +208,7 @@ smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
smb_llist_exit(bucket);
if (newlease != NULL) {
- mutex_destroy(&newlease->ls_mutex);
- kmem_cache_free(smb_lease_cache, newlease);
+ lease_destroy(newlease);
}
if (lease != NULL) {
@@ -213,9 +223,11 @@ smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
* Find the lease for a given: client_uuid, lease_key
* Returns the lease with a new ref.
*/
-smb_lease_t *
-smb2_lease_lookup(smb_server_t *sv, uint8_t *clnt_uuid, uint8_t *lease_key)
+static smb_lease_t *
+lease_lookup(smb_request_t *sr, uint8_t *lease_key)
{
+ smb_server_t *sv = sr->sr_server;
+ uint8_t *clnt_uuid = sr->session->clnt_uuid;
smb_hash_t *ht = sv->sv_lease_ht;
smb_llist_t *bucket;
smb_lease_t *lease;
@@ -241,57 +253,36 @@ smb2_lease_lookup(smb_server_t *sv, uint8_t *clnt_uuid, uint8_t *lease_key)
}
/*
- * Find an smb_ofile_t in the current tree that shares the
- * specified lease and holds the oplock for the lease.
- * If lease not found, NT_STATUS_OBJECT_NAME_NOT_FOUND.
- * If no ofile (on the lease) holds the oplock, NT_STATUS_UNSUCCESSFUL.
+ * Find the oplock smb_ofile_t for the specified lease.
+ * If no such ofile, NT_STATUS_UNSUCCESSFUL.
* On success, ofile (held) in sr->fid_ofile.
*/
static uint32_t
-find_oplock_ofile(smb_request_t *sr, uint8_t *lease_key)
+lease_find_oplock(smb_request_t *sr, smb_lease_t *lease)
{
- smb_tree_t *tree = sr->tid_tree;
- smb_lease_t *lease;
- smb_llist_t *of_list;
+ smb_node_t *node = lease->ls_node;
smb_ofile_t *o;
- uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
-
- SMB_TREE_VALID(tree);
- of_list = &tree->t_ofile_list;
-
- smb_llist_enter(of_list, RW_READER);
- for (o = smb_llist_head(of_list); o != NULL;
- o = smb_llist_next(of_list, o)) {
+ uint32_t status = NT_STATUS_UNSUCCESSFUL;
- ASSERT(o->f_magic == SMB_OFILE_MAGIC);
- ASSERT(o->f_tree == tree);
-
- DTRACE_PROBE1(every_ofile, smb_ofile_t *, o);
- if ((lease = o->f_lease) == NULL)
- continue; // no lease
-
- if (bcmp(lease->ls_key, lease_key, UUID_LEN) != 0)
- continue; // wrong lease
+ ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
+ ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+ ASSERT(sr->fid_ofile == NULL);
+ FOREACH_NODE_OFILE(node, o) {
+ if (o->f_lease != lease)
+ continue;
+ if (o != lease->ls_oplock_ofile)
+ continue;
/*
- * Now we know the lease exists, so if we don't
- * find the ofile that has the oplock, return:
+ * Found the ofile holding the oplock
+ * This hold released in smb_request_free
*/
- status = NT_STATUS_UNSUCCESSFUL;
-
- DTRACE_PROBE2(lease_ofile, smb_lease_t *, lease,
- smb_ofile_t *, o);
- if (lease->ls_oplock_ofile != o)
- continue; // not oplock holder
-
- /* Found the ofile holding the oplock */
- if (smb_ofile_hold(o)) {
+ if (smb_ofile_hold_olbrk(o)) {
sr->fid_ofile = o;
status = NT_STATUS_SUCCESS;
break;
}
}
- smb_llist_exit(of_list);
return (status);
}
@@ -300,15 +291,16 @@ find_oplock_ofile(smb_request_t *sr, uint8_t *lease_key)
* This is called by smb2_oplock_break_ack when the struct size
* indicates this is a lease break (SZ_LEASE). See:
* [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
+ * This is an "Ack" from the client.
*/
smb_sdrc_t
smb2_lease_break_ack(smb_request_t *sr)
{
+ smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
smb_lease_t *lease;
+ smb_node_t *node;
smb_ofile_t *ofile;
- uint8_t LeaseKey[UUID_LEN];
uint32_t LeaseState;
- uint32_t LeaseBreakTo;
uint32_t status;
int rc = 0;
@@ -324,56 +316,101 @@ smb2_lease_break_ack(smb_request_t *sr)
&sr->smb_data, "6.#cl8.",
/* reserved 6. */
UUID_LEN, /* # */
- LeaseKey, /* c */
- &LeaseState); /* l */
+ olbrk->LeaseKey, /* c */
+ &olbrk->NewLevel); /* l */
/* duration 8. */
if (rc != 0)
return (SDRC_ERROR);
+ LeaseState = olbrk->NewLevel;
+
+ /*
+ * Find the lease via the given key.
+ */
+ lease = lease_lookup(sr, olbrk->LeaseKey);
+ if (lease == NULL) {
+ /*
+ * It's unusual to skip the dtrace start/done
+ * probes like this, but trying to run them
+ * with no lease->node would be complex and
+ * would not show anything particularly useful.
+ * Do the start probe after we find the ofile.
+ */
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+ // Note: lease ref; smb_lease_rele() below.
+ node = lease->ls_node;
+
+ /*
+ * Find the leased oplock. Hold locks so it can't move
+ * until we're done with ACK-break processing.
+ */
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
- status = find_oplock_ofile(sr, LeaseKey);
+ status = lease_find_oplock(sr, lease);
+ /* Normally have sr->fid_ofile now. */
DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
- if (status != 0)
+
+ if (status != 0) {
+ /* Leased oplock not found. Must have closed. */
goto errout;
+ }
- /* Success, so have sr->fid_ofile and lease */
+ /* Success, so have sr->fid_ofile */
ofile = sr->fid_ofile;
- lease = ofile->f_lease;
- /* Either this ACK is unsolicited, or we timed out waiting. */
- if (lease->ls_breaking == 0) {
+ if (lease->ls_breaking == B_FALSE) {
+ /*
+ * This ACK is either unsolicited or too late,
+ * eg. we timed out the ACK and did it locally.
+ */
status = NT_STATUS_UNSUCCESSFUL;
goto errout;
}
/*
- * Process the lease break ack.
- *
* If the new LeaseState has any bits in excess of
* the lease state we sent in the break, error...
*/
- LeaseBreakTo = (lease->ls_breaking >> BREAK_SHIFT) &
- OPLOCK_LEVEL_CACHE_MASK;
- if ((LeaseState & ~LeaseBreakTo) != 0) {
+ if ((LeaseState & ~(lease->ls_breakto)) != 0) {
status = NT_STATUS_REQUEST_NOT_ACCEPTED;
goto errout;
}
- lease->ls_breaking = 0;
+ /*
+ * Process the lease break ack.
+ *
+ * Clear breaking flags before we ack,
+ * because ack might set those.
+ * Signal both CVs, out of paranoia.
+ */
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+ lease->ls_breaking = B_FALSE;
+ cv_broadcast(&lease->ls_ack_cv);
LeaseState |= OPLOCK_LEVEL_GRANULAR;
status = smb_oplock_ack_break(sr, ofile, &LeaseState);
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- (void) smb2sr_go_async(sr);
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = NT_STATUS_SUCCESS;
- }
- lease->ls_state = LeaseState & OPLOCK_LEVEL_CACHE_MASK;
+ ofile->f_oplock.og_state = LeaseState;
+ lease->ls_state = LeaseState;
+ /* ls_epoch does not change here */
+
+ if (ofile->dh_persist)
+ smb2_dh_update_oplock(sr, ofile);
errout:
sr->smb2_status = status;
DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+ smb2_lease_rele(lease);
+
if (status) {
smb2sr_put_error(sr, status);
return (SDRC_SUCCESS);
@@ -389,7 +426,7 @@ errout:
SSZ_LEASE_ACK, /* w */
/* reserved 6. */
UUID_LEN, /* # */
- LeaseKey, /* c */
+ olbrk->LeaseKey, /* c */
LeaseState); /* l */
/* duration 8. */
@@ -404,25 +441,23 @@ errout:
*
* [MS-SMB2] 2.2.23.2 Lease Break Notification
*/
-void
-smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel,
- boolean_t AckReq)
+static void
+smb2_lease_break_notification(smb_request_t *sr,
+ uint32_t OldLevel, uint32_t NewLevel,
+ uint16_t Epoch, boolean_t AckReq)
{
smb_lease_t *ls = sr->fid_ofile->f_lease;
- uint32_t oldcache;
- uint32_t newcache;
- uint16_t Epoch;
- uint16_t Flags;
+ uint16_t Flags = 0;
/*
- * Convert internal level to SMB2
+ * Convert internal lease info to SMB2
*/
- oldcache = ls->ls_state & OPLOCK_LEVEL_CACHE_MASK;
- newcache = NewLevel & OPLOCK_LEVEL_CACHE_MASK;
+ if (AckReq)
+ Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
if (ls->ls_version < 2)
Epoch = 0;
- else
- Epoch = ls->ls_epoch;
+ OldLevel &= OPLOCK_LEVEL_CACHE_MASK;
+ NewLevel &= OPLOCK_LEVEL_CACHE_MASK;
/*
* SMB2 Header
@@ -441,7 +476,6 @@ smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel,
* [MS-SMB2] says the current lease state preceeds the
* new lease state, but that looks like an error...
*/
- Flags = AckReq ? SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED : 0;
(void) smb_mbc_encodef(
&sr->reply, "wwl#cll4.4.4.",
SSZ_LEASE_BRK, /* w */
@@ -449,17 +483,271 @@ smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel,
Flags, /* l */
SMB_LEASE_KEY_SZ, /* # */
ls->ls_key, /* c */
- oldcache, /* cur.st l */
- newcache); /* new.st l */
+ OldLevel, /* cur.st l */
+ NewLevel); /* new.st l */
/* reserved (4.4.4.) */
}
/*
+ * Do our best to send a lease break message to the client.
+ * When we get to multi-channel, this is supposed to try
+ * every channel before giving up. For now, try every
+ * connected session with an ofile sharing this lease.
+ *
+ * If this ofile has a valid session, try that first.
+ * Otherwise look on the node list for other ofiles with
+ * the same lease and a connected session.
+ */
+static int
+lease_send_any_cn(smb_request_t *sr)
+{
+ smb_ofile_t *o;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_lease_t *lease = ofile->f_lease;
+ smb_node_t *node = ofile->f_node;
+ int rc = ENOTCONN;
+
+ /*
+ * If the passed oplock ofile has a session,
+ * this IF expression will be true.
+ */
+ if (sr->session == ofile->f_session) {
+ rc = smb_session_send(sr->session, 0, &sr->reply);
+ if (rc == 0)
+ return (rc);
+ }
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ FOREACH_NODE_OFILE(node, o) {
+ if (o->f_lease != lease)
+ continue;
+ if (smb_ofile_hold(o)) {
+ /* Has a session. */
+ rc = smb_session_send(o->f_session, 0, &sr->reply);
+ smb_llist_post(&node->n_ofile_list, o,
+ smb_ofile_release_LL);
+ }
+ if (rc == 0)
+ break;
+ }
+ smb_llist_exit(&node->n_ofile_list);
+
+ return (rc);
+}
+
+/*
+ * See smb_llist_post on node->n_ofile_list below.
+ * Can't call smb_ofile_close with that list entered.
+ */
+static void
+lease_ofile_close_rele(void *arg)
+{
+ smb_ofile_t *of = (smb_ofile_t *)arg;
+
+ smb_ofile_close(of, 0);
+ smb_ofile_release(of);
+}
+
+/*
+ * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
+ * If no connection, for each Open in Lease.LeaseOpens,
+ * the server MUST close the Open as specified in sec...
+ * for the following cases:
+ * - Open.IsDurable, Open.IsResilient, and
+ * Open.IsPersistent are all FALSE.
+ * - Open.IsDurable is TRUE and Lease.BreakToLeaseState
+ * does not contain SMB2_LEASE_HANDLE_CACHING and
+ */
+static void
+lease_close_notconn(smb_request_t *sr, uint32_t NewLevel)
+{
+ smb_ofile_t *o;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_lease_t *lease = ofile->f_lease;
+ smb_node_t *node = ofile->f_node;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ FOREACH_NODE_OFILE(node, o) {
+ if (o->f_lease != lease)
+ continue;
+ if (o->f_oplock_closing)
+ continue;
+ if (o->dh_persist)
+ continue;
+ if (o->dh_vers == SMB2_RESILIENT)
+ continue;
+ if (o->dh_vers == SMB2_NOT_DURABLE ||
+ (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) {
+ if (smb_ofile_hold_olbrk(o)) {
+ smb_llist_post(&node->n_ofile_list, o,
+ lease_ofile_close_rele);
+ }
+ }
+ }
+ smb_llist_exit(&node->n_ofile_list);
+}
+
+/*
+ * Send a lease break over the wire, or if we can't,
+ * then process the lease break locally.
+ *
+ * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
+ *
+ * This is mostly similar to smb2_oplock_send_break()
+ * See top comment there about the design.
+ *
+ * Differences beween a lease break and oplock break:
+ *
+ * Leases are an SMB-level mechanism whereby multiple open
+ * SMB file handles can share an oplock. All SMB handles
+ * on the lease enjoy the same caching rights. Down at the
+ * file-system level, just one oplock holds the cache rights
+ * for a lease, but (this is the tricky part) that oplock can
+ * MOVE among the SMB file handles sharing the lease. Such
+ * oplock moves can happen when a handle is closed (if that
+ * handle is the one with the oplock) or when a new open on
+ * the lease causes an upgrade of the caching rights.
+ *
+ * We have to deal here with lease movement because this call
+ * happens asynchronously after the smb_oplock_ind_break call,
+ * meaning that the oplock for the lease may have moved by the
+ * time this runs. In addition, the ofile holding the oplock
+ * might not be the best one to use to send a lease break.
+ * If the oplock is held by a handle that's "orphaned" and
+ * there are other handles on the lease with active sessions,
+ * we want to send the lease break on an active session.
+ *
+ * Also note: NewLevel (as provided by smb_oplock_ind_break etc.)
+ * does NOT include the GRANULAR flag. This level is expected to
+ * keep track of how each oplock was acquired (by lease or not)
+ * and put the GRANULAR flag back in when appropriate.
+ */
+void
+smb2_lease_send_break(smb_request_t *sr)
+{
+ smb_ofile_t *old_ofile;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_node_t *node = ofile->f_node;
+ smb_lease_t *lease = ofile->f_lease;
+ smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
+ boolean_t AckReq = olbrk->AckRequired;
+ uint32_t OldLevel = olbrk->OldLevel;
+ uint32_t NewLevel = olbrk->NewLevel;
+ uint32_t status;
+ int rc;
+
+ NewLevel |= OPLOCK_LEVEL_GRANULAR;
+
+ /*
+ * Build the break message in sr->reply.
+ * It's free'd in smb_request_free().
+ * Always an SMB2 lease here.
+ */
+ sr->reply.max_bytes = MLEN;
+ smb2_lease_break_notification(sr,
+ OldLevel, NewLevel, lease->ls_epoch, AckReq);
+
+ /*
+ * Try to send the break message to the client,
+ * on any connection with this lease.
+ */
+ rc = lease_send_any_cn(sr);
+ if (rc != 0) {
+ /*
+ * We were unable to send the oplock break request,
+ * presumably because the connection is gone.
+ * Close uninteresting handles.
+ */
+ lease_close_notconn(sr, NewLevel);
+ /* Note: some handles may remain on the lease. */
+ if (!AckReq)
+ return;
+ /* Do local Ack below. */
+ } else {
+ /*
+ * OK, we were able to send the break message.
+ * If no ack. required, we're done.
+ */
+ if (!AckReq)
+ return;
+
+ /*
+ * We're expecting an ACK. Wait in this thread
+ * so we can log clients that don't respond.
+ */
+ status = smb_oplock_wait_ack(sr, NewLevel);
+ if (status == 0)
+ return;
+
+ cmn_err(CE_NOTE, "clnt %s oplock break timeout",
+ sr->session->ip_addr_str);
+ DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
+
+ /*
+ * Will do local ack below. Note, after timeout,
+ * do a break to none or "no caching" regardless
+ * of what the passed in cache level was.
+ * That means: clear all except GRANULAR.
+ */
+ NewLevel = OPLOCK_LEVEL_GRANULAR;
+ }
+
+ /*
+ * Do the ack locally.
+ *
+ * Find the ofile with the leased oplock
+ * (may have moved before we took locks)
+ */
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ old_ofile = ofile;
+ sr->fid_ofile = NULL;
+ status = lease_find_oplock(sr, lease);
+ if (status != 0) {
+ /* put back old_ofile */
+ sr->fid_ofile = old_ofile;
+ goto unlock_out;
+ }
+ smb_llist_post(&node->n_ofile_list, old_ofile,
+ smb_ofile_release_LL);
+
+ ofile = sr->fid_ofile;
+
+ /*
+ * Now continue like the non-lease code
+ */
+ ofile->f_oplock.og_breaking = B_FALSE;
+ lease->ls_breaking = B_FALSE;
+ cv_broadcast(&lease->ls_ack_cv);
+
+ status = smb_oplock_ack_break(sr, ofile, &NewLevel);
+
+ ofile->f_oplock.og_state = NewLevel;
+ lease->ls_state = NewLevel;
+ /* ls_epoch does not change here */
+
+ if (ofile->dh_persist)
+ smb2_dh_update_oplock(sr, ofile);
+
+unlock_out:
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+#ifdef DEBUG
+ if (status != 0) {
+ cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
+ sr->session->ip_addr_str, status);
+ }
+#endif
+}
+
+/*
* Client has an open handle and requests a lease.
* Convert SMB2 lease request info in to internal form,
* call common oplock code, convert result to SMB2.
*
- * If necessary, "go async" here.
+ * If necessary, "go async" here (at the end).
*/
void
smb2_lease_acquire(smb_request_t *sr)
@@ -467,6 +755,7 @@ smb2_lease_acquire(smb_request_t *sr)
smb_arg_open_t *op = &sr->arg.open;
smb_ofile_t *ofile = sr->fid_ofile;
smb_lease_t *lease = ofile->f_lease;
+ smb_node_t *node = ofile->f_node;
uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
uint32_t have, want; /* lease flags */
boolean_t NewGrant = B_FALSE;
@@ -510,15 +799,25 @@ smb2_lease_acquire(smb_request_t *sr)
}
/*
- * Disallow downgrade
+ * Using the "Locks Held" (LH) variant of smb_oplock_request
+ * below so things won't change underfoot.
+ */
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ /*
+ * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts
*
- * Note that open with a lease is not allowed to turn off
- * any cache rights. If the client tries to "downgrade",
- * any bits, just return the existing lease cache bits.
+ * If the caching state requested in LeaseState of the (create ctx)
+ * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE,
+ * the server MUST NOT promote Lease.LeaseState. If the lease state
+ * requested is a superset of Lease.LeaseState and Lease.Breaking is
+ * FALSE, the server MUST request promotion of the lease state from
+ * the underlying object store to the new caching state.
*/
have = lease->ls_state & CACHE_RWH;
want = op->op_oplock_state & CACHE_RWH;
- if ((have & ~want) != 0) {
+ if ((have & ~want) != 0 || lease->ls_breaking) {
op->op_oplock_state = have |
OPLOCK_LEVEL_GRANULAR;
goto done;
@@ -543,11 +842,9 @@ smb2_lease_acquire(smb_request_t *sr)
* Try exclusive (request is RW or RWH)
*/
if ((op->op_oplock_state & WRITE_CACHING) != 0) {
- want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
- goto done;
+ /* Alread checked (want & ~have) */
- status = smb_oplock_request(sr, ofile,
+ status = smb_oplock_request_LH(sr, ofile,
&op->op_oplock_state);
if (status == NT_STATUS_SUCCESS ||
status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
@@ -584,10 +881,10 @@ smb2_lease_acquire(smb_request_t *sr)
*/
if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
+ if ((want & ~have) == 0)
goto done;
- status = smb_oplock_request(sr, ofile,
+ status = smb_oplock_request_LH(sr, ofile,
&op->op_oplock_state);
if (status == NT_STATUS_SUCCESS ||
status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
@@ -599,6 +896,7 @@ smb2_lease_acquire(smb_request_t *sr)
* We did not get "RH", probably because
* ther were (old style) Level II oplocks.
* Continue, try for just read.
+ * Again, re-init op_oplock_state
*/
op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
(op->lease_state & CACHE_R);
@@ -609,10 +907,10 @@ smb2_lease_acquire(smb_request_t *sr)
*/
if ((op->op_oplock_state & READ_CACHING) != 0) {
want = op->op_oplock_state & CACHE_RWH;
- if (have == want)
+ if ((want & ~have) == 0)
goto done;
- status = smb_oplock_request(sr, ofile,
+ status = smb_oplock_request_LH(sr, ofile,
&op->op_oplock_state);
if (status == NT_STATUS_SUCCESS ||
status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
@@ -634,26 +932,32 @@ smb2_lease_acquire(smb_request_t *sr)
op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
done:
- if (NewGrant) {
- /*
- * After a new oplock grant, the status return
- * may indicate we need to wait for breaks.
- */
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- (void) smb2sr_go_async(sr);
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = NT_STATUS_SUCCESS;
- }
- ASSERT(status == NT_STATUS_SUCCESS);
+ /*
+ * Only success cases get here
+ */
- /*
- * Keep track of what we got (in lease->ls_state)
- * so we'll know what we last told the client.
- */
- mutex_enter(&lease->ls_mutex);
- lease->ls_state = op->op_oplock_state & CACHE_RWH;
+ /*
+ * Keep track of what we got (ofile->f_oplock.og_state etc)
+ * so we'll know what we had when sending a break later.
+ * Also keep a copy of some things in the lease.
+ *
+ * Not using og_dialect here, as ofile->f_lease tells us
+ * this has to be using granular oplocks.
+ */
+ if (NewGrant) {
+ ofile->f_oplock.og_state = op->op_oplock_state;
+ ofile->f_oplock.og_breakto = op->op_oplock_state;
+ ofile->f_oplock.og_breaking = B_FALSE;
+
+ lease->ls_oplock_ofile = ofile;
+ lease->ls_state = ofile->f_oplock.og_state;
+ lease->ls_breakto = ofile->f_oplock.og_breakto;
+ lease->ls_breaking = B_FALSE;
lease->ls_epoch++;
- mutex_exit(&lease->ls_mutex);
+
+ if (ofile->dh_persist) {
+ smb2_dh_update_oplock(sr, ofile);
+ }
}
/*
@@ -665,16 +969,42 @@ done:
SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
op->lease_epoch = lease->ls_epoch;
op->lease_version = lease->ls_version;
+
+ /*
+ * End of lock-held region
+ */
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+ /*
+ * After a new oplock grant, the status return
+ * may indicate we need to wait for breaks.
+ */
+ if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ (void) smb2sr_go_async(sr);
+ (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
+ }
}
/*
* This ofile has a lease and is about to close.
* Called by smb_ofile_close when there's a lease.
*
+ * Note that a client may close an ofile in response to an
+ * oplock break or lease break intead of doing an Ack break,
+ * so this must wake anything that might be waiting on an ack
+ * when the last close of a lease happens.
+ *
* With leases, just one ofile on a lease owns the oplock.
* If an ofile with a lease is closed and it's the one that
* owns the oplock, try to move the oplock to another ofile
* on the same lease.
+ *
+ * Would prefer that we could just use smb_ofile_hold_olbrk
+ * to select a suitable destination for the move, but this
+ * is called while holding the owning tree ofile list etc
+ * which can cause deadlock as described in illumos 13850
+ * when smb_ofile_hold_olbrk has to wait. XXX todo
*/
void
smb2_lease_ofile_close(smb_ofile_t *ofile)
@@ -686,6 +1016,12 @@ smb2_lease_ofile_close(smb_ofile_t *ofile)
ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
/*
* If this ofile was not the oplock owner for this lease,
* we can leave things as they are.
@@ -695,7 +1031,7 @@ smb2_lease_ofile_close(smb_ofile_t *ofile)
/*
* Find another ofile to which we can move the oplock.
- * The ofile must be open and allow a new ref.
+ * First try for one that's open. Usually find one.
*/
FOREACH_NODE_OFILE(node, o) {
if (o == ofile)
@@ -704,17 +1040,59 @@ smb2_lease_ofile_close(smb_ofile_t *ofile)
continue;
if (o->f_oplock_closing)
continue;
- /* If we can get a hold, use this ofile. */
- if (smb_ofile_hold(o))
- break;
+
+ mutex_enter(&o->f_mutex);
+ if (o->f_state == SMB_OFILE_STATE_OPEN) {
+ smb_oplock_move(node, ofile, o);
+ lease->ls_oplock_ofile = o;
+ mutex_exit(&o->f_mutex);
+ return;
+ }
+ mutex_exit(&o->f_mutex);
}
- if (o == NULL) {
- /* Normal for last close on a lease. */
- lease->ls_oplock_ofile = NULL;
- return;
+
+ /*
+ * Now try for one that's orphaned etc.
+ */
+ FOREACH_NODE_OFILE(node, o) {
+ if (o == ofile)
+ continue;
+ if (o->f_lease != lease)
+ continue;
+ if (o->f_oplock_closing)
+ continue;
+
+ /*
+ * Allow most states as seen in smb_ofile_hold_olbrk
+ * without waiting for "_reconnect" or "_saving".
+ * Skip "_expired" because that's about to close.
+ * This is OK because just swapping the oplock state
+ * between two ofiles does not interfere with the
+ * dh_save or reconnect code paths.
+ */
+ mutex_enter(&o->f_mutex);
+ switch (o->f_state) {
+ case SMB_OFILE_STATE_OPEN:
+ case SMB_OFILE_STATE_SAVE_DH:
+ case SMB_OFILE_STATE_SAVING:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_RECONNECT:
+ smb_oplock_move(node, ofile, o);
+ lease->ls_oplock_ofile = o;
+ mutex_exit(&o->f_mutex);
+ return;
+ }
+ mutex_exit(&o->f_mutex);
}
- smb_oplock_move(node, ofile, o);
- lease->ls_oplock_ofile = o;
- smb_ofile_release(o);
+ /*
+ * Normal for last close on a lease.
+ * Wakeup ACK waiters too.
+ */
+ lease->ls_state = 0;
+ lease->ls_breakto = 0;
+ lease->ls_breaking = B_FALSE;
+ cv_broadcast(&lease->ls_ack_cv);
+
+ lease->ls_oplock_ofile = NULL;
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_oplock.c b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
index f8317b2e81..0d807870bc 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
@@ -11,7 +11,7 @@
/*
* Copyright 2020 Tintri by DDN, Inc. All rights reserved.
- * Copyright 2019 RackTop Systems.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -19,6 +19,7 @@
*/
#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_oplock.h>
#define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
@@ -30,11 +31,15 @@
* SMB2 Oplock Break Acknowledgement
* [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
* Called via smb2_disp_table[]
+ * This is an "Ack" from the client.
*/
smb_sdrc_t
smb2_oplock_break_ack(smb_request_t *sr)
{
+ smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
+ smb_node_t *node;
smb_ofile_t *ofile;
+ smb_oplock_grant_t *og;
smb2fid_t smb2fid;
uint32_t status;
uint32_t NewLevel;
@@ -72,16 +77,8 @@ smb2_oplock_break_ack(smb_request_t *sr)
if (rc != 0)
return (SDRC_ERROR);
- /* Find the ofile */
- status = smb2sr_lookup_fid(sr, &smb2fid);
- /* Success or NT_STATUS_FILE_CLOSED */
-
- DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
- if (status != 0)
- goto errout;
-
/*
- * Process an (old-style) oplock break ack.
+ * Convert SMB oplock level to internal form.
*/
switch (smbOplockLevel) {
case SMB2_OPLOCK_LEVEL_NONE: /* 0x00 */
@@ -96,50 +93,91 @@ smb2_oplock_break_ack(smb_request_t *sr)
case SMB2_OPLOCK_LEVEL_BATCH: /* 0x09 */
NewLevel = OPLOCK_LEVEL_BATCH;
break;
+
+ /* Note: _LEVEL_LEASE is not valid here. */
case SMB2_OPLOCK_LEVEL_LEASE: /* 0xFF */
- NewLevel = OPLOCK_LEVEL_NONE;
- break;
default:
+ /*
+ * Impossible NewLevel here, will cause
+ * NT_STATUS_INVALID_PARAMETER below.
+ */
+ NewLevel = OPLOCK_LEVEL_GRANULAR;
+ break;
+ }
+
+ /* for dtrace */
+ olbrk->NewLevel = NewLevel;
+
+ /* Find the ofile */
+ status = smb2sr_lookup_fid(sr, &smb2fid);
+ /* Success or NT_STATUS_FILE_CLOSED */
+
+ DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
+
+ if (status != 0) {
+ /* lookup fid failed */
+ goto errout;
+ }
+
+ if (NewLevel == OPLOCK_LEVEL_GRANULAR) {
+ /* Switch above got invalid smbOplockLevel */
status = NT_STATUS_INVALID_PARAMETER;
goto errout;
}
+ /* Success, so have sr->fid_ofile */
ofile = sr->fid_ofile;
- if (ofile->f_oplock.og_breaking == 0) {
+ og = &ofile->f_oplock;
+ node = ofile->f_node;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ if (og->og_breaking == B_FALSE) {
/*
* This is an unsolicited Ack. (There is no
* outstanding oplock break in progress now.)
* There are WPTS tests that care which error
* is returned. See [MS-SMB2] 3.3.5.22.1
*/
- if (smbOplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto errout;
- }
- if (NewLevel >= (ofile->f_oplock.og_state &
- OPLOCK_LEVEL_TYPE_MASK)) {
+ if (NewLevel >= (og->og_state & OPLOCK_LEVEL_TYPE_MASK)) {
status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
- goto errout;
+ goto unlock_out;
}
status = NT_STATUS_INVALID_DEVICE_STATE;
- goto errout;
+ goto unlock_out;
}
- ofile->f_oplock.og_breaking = 0;
+
+ /*
+ * Process the oplock break ack.
+ *
+ * Clear breaking flags before we ack,
+ * because ack might set those.
+ */
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
status = smb_oplock_ack_break(sr, ofile, &NewLevel);
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- status = smb2sr_go_async(sr);
- if (status != 0)
- goto errout;
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = 0;
- }
- if (status != 0) {
- NewLevel = OPLOCK_LEVEL_NONE;
- goto errout;
- }
ofile->f_oplock.og_state = NewLevel;
+ if (ofile->dh_persist)
+ smb2_dh_update_oplock(sr, ofile);
+
+unlock_out:
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+errout:
+ sr->smb2_status = status;
+ DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
+ if (status) {
+ smb2sr_put_error(sr, status);
+ return (SDRC_SUCCESS);
+ }
+
+ /*
+ * Convert internal oplock state back to SMB form.
+ */
switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
case OPLOCK_LEVEL_NONE:
smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
@@ -159,14 +197,6 @@ smb2_oplock_break_ack(smb_request_t *sr)
break;
}
-errout:
- sr->smb2_status = status;
- DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
- if (status) {
- smb2sr_put_error(sr, status);
- return (SDRC_SUCCESS);
- }
-
/*
* Encode an SMB2 Oplock Break Ack response
* [MS-SMB2] 2.2.25.1
@@ -187,7 +217,7 @@ errout:
* the SMB2 header and everything, in sr->reply.
* The caller will send it and free the request.
*/
-void
+static void
smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
{
smb_ofile_t *ofile = sr->fid_ofile;
@@ -237,6 +267,137 @@ smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
}
/*
+ * Send an oplock break over the wire, or if we can't,
+ * then process the oplock break locally.
+ *
+ * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
+ *
+ * Note: When "AckRequired" is set, and we're for any reason
+ * unable to communicate with the client so that they do an
+ * "oplock break ACK", then we absolutely MUST do a local ACK
+ * for this break indication (or close the ofile).
+ *
+ * The file-system level oplock code (smb_cmn_oplock.c)
+ * requires these ACK calls to clear "breaking" flags.
+ *
+ * This is called either from smb_oplock_async_break via a
+ * taskq job scheduled in smb_oplock_ind_break, or from the
+ * smb2sr_append_postwork() mechanism when we're doing a
+ * "break in ack", via smb_oplock_ind_break_in_ack.
+ *
+ * This runs much like other smb_request_t handlers, in the
+ * context of a worker task that calls with no locks held.
+ *
+ * Note that we have sr->fid_ofile here but all the other
+ * normal sr members may be NULL: uid_user, tid_tree.
+ * Also sr->session may or may not be the same session as
+ * the ofile came from (ofile->f_session) depending on
+ * whether this is a "live" open or an orphaned DH,
+ * where ofile->f_session will be NULL.
+ */
+void
+smb2_oplock_send_break(smb_request_t *sr)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_node_t *node = ofile->f_node;
+ uint32_t NewLevel = sr->arg.olbrk.NewLevel;
+ boolean_t AckReq = sr->arg.olbrk.AckRequired;
+ uint32_t status;
+ int rc;
+
+ /*
+ * Build the break message in sr->reply.
+ * It's free'd in smb_request_free().
+ * Always SMB2 oplock here (no lease)
+ */
+ sr->reply.max_bytes = MLEN;
+ smb2_oplock_break_notification(sr, NewLevel);
+
+ /*
+ * Try to send the break message to the client.
+ * If connected, this IF body will be true.
+ */
+ if (sr->session == ofile->f_session)
+ rc = smb_session_send(sr->session, 0, &sr->reply);
+ else
+ rc = ENOTCONN;
+
+ if (rc != 0) {
+ /*
+ * We were unable to send the oplock break request,
+ * presumably because the connection is gone.
+ *
+ * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
+ * If no connection is available, Open.IsResilient is FALSE,
+ * Open.IsDurable is FALSE, and Open.IsPersistent is FALSE,
+ * the server SHOULD close the Open as specified in...
+ */
+ if (ofile->dh_persist == B_FALSE &&
+ ofile->dh_vers != SMB2_RESILIENT &&
+ (ofile->dh_vers == SMB2_NOT_DURABLE ||
+ (NewLevel & OPLOCK_LEVEL_BATCH) == 0)) {
+ smb_ofile_close(ofile, 0);
+ return;
+ }
+ /* Keep this (durable) open. */
+ if (!AckReq)
+ return;
+ /* Do local Ack below. */
+ } else {
+ /*
+ * OK, we were able to send the break message.
+ * If no ack. required, we're done.
+ */
+ if (!AckReq)
+ return;
+
+ /*
+ * We're expecting an ACK. Wait in this thread
+ * so we can log clients that don't respond.
+ */
+ status = smb_oplock_wait_ack(sr, NewLevel);
+ if (status == 0)
+ return;
+
+ cmn_err(CE_NOTE, "clnt %s oplock break timeout",
+ sr->session->ip_addr_str);
+ DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
+
+ /*
+ * Will do local ack below. Note, after timeout,
+ * do a break to none or "no caching" regardless
+ * of what the passed in cache level was.
+ */
+ NewLevel = OPLOCK_LEVEL_NONE;
+ }
+
+ /*
+ * Do the ack locally.
+ */
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+
+ status = smb_oplock_ack_break(sr, ofile, &NewLevel);
+
+ ofile->f_oplock.og_state = NewLevel;
+ if (ofile->dh_persist)
+ smb2_dh_update_oplock(sr, ofile);
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+#ifdef DEBUG
+ if (status != 0) {
+ cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
+ sr->session->ip_addr_str, status);
+ }
+#endif
+}
+
+/*
* Client has an open handle and requests an oplock.
* Convert SMB2 oplock request info in to internal form,
* call common oplock code, convert result to SMB2.
@@ -289,6 +450,8 @@ smb2_oplock_acquire(smb_request_t *sr)
/*
* Tree options may force shared oplocks,
* in which case we reduce the request.
+ * Can't get here with LEVEL_NONE, so
+ * this can only decrease the level.
*/
if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
op->op_oplock_state = OPLOCK_LEVEL_TWO;
@@ -315,41 +478,39 @@ smb2_oplock_acquire(smb_request_t *sr)
}
/*
- * Either of the above may have returned the
- * status code that says we should wait.
- */
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- (void) smb2sr_go_async(sr);
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = 0;
- }
-
- /*
- * Keep track of what we got (in ofile->f_oplock.og_state)
+ * Keep track of what we got (ofile->f_oplock.og_state etc)
* so we'll know what we had when sending a break later.
* The og_dialect here is the oplock dialect, not the
* SMB dialect. No lease here, so SMB 2.0.
*/
- ofile->f_oplock.og_dialect = SMB_VERS_2_002;
switch (status) {
case NT_STATUS_SUCCESS:
- ofile->f_oplock.og_state = op->op_oplock_state;
+ case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
+ ofile->f_oplock.og_dialect = SMB_VERS_2_002;
+ ofile->f_oplock.og_state = op->op_oplock_state;
+ ofile->f_oplock.og_breakto = op->op_oplock_state;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ if (ofile->dh_persist) {
+ smb2_dh_update_oplock(sr, ofile);
+ }
break;
+
case NT_STATUS_OPLOCK_NOT_GRANTED:
- ofile->f_oplock.og_state = 0;
op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
return;
+
default:
/* Caller did not check args sufficiently? */
cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
sr->session->ip_addr_str, status);
- ofile->f_oplock.og_state = 0;
+ DTRACE_PROBE2(other__error, smb_request_t *, sr,
+ uint32_t, status);
op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
return;
}
/*
- * Have STATUS_SUCCESS
+ * Only success cases get here
* Convert internal oplock state to SMB2
*/
if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
@@ -364,6 +525,15 @@ smb2_oplock_acquire(smb_request_t *sr)
} else {
op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
}
+
+ /*
+ * An smb_oplock_reqest call may have returned the
+ * status code that says we should wait.
+ */
+ if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ (void) smb2sr_go_async(sr);
+ (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
+ }
}
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb_cmn_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_cmn_oplock.c
index bee55e2e60..840ab49367 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_cmn_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_cmn_oplock.c
@@ -315,10 +315,30 @@ uint32_t
smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
{
smb_node_t *node = ofile->f_node;
+ uint32_t status;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ status = smb_oplock_request_LH(sr, ofile, statep);
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+ return (status);
+}
+
+uint32_t
+smb_oplock_request_LH(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
+{
+ smb_node_t *node = ofile->f_node;
uint32_t type = *statep & OPLOCK_LEVEL_TYPE_MASK;
uint32_t level = *statep & OPLOCK_LEVEL_CACHE_MASK;
uint32_t status;
+ ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
+ ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+
*statep = LEVEL_NONE;
/*
@@ -343,9 +363,6 @@ smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
return (NT_STATUS_OPLOCK_NOT_GRANTED);
}
- smb_llist_enter(&node->n_ofile_list, RW_READER);
- mutex_enter(&node->n_oplock.ol_mutex);
-
/*
* If Type is LEVEL_ONE or LEVEL_BATCH:
* The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
@@ -495,25 +512,15 @@ smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
break;
}
- /* Give caller back the "Granular" bit. */
- if (status == NT_STATUS_SUCCESS) {
+ /*
+ * Give caller back the "Granular" bit, eg. when
+ * NT_STATUS_SUCCESS or NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
+ */
+ if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_SUCCESS) {
*statep |= LEVEL_GRANULAR;
-
- /*
- * The oplock lease may have moved to this ofile. Update.
- * Minor violation of layering here (leases vs oplocks)
- * but we want this update coverd by the oplock mutex.
- */
-#ifndef TESTJIG
- if (ofile->f_lease != NULL)
- ofile->f_lease->ls_oplock_ofile = ofile;
-#endif
}
out:
- mutex_exit(&node->n_oplock.ol_mutex);
- smb_llist_exit(&node->n_ofile_list);
-
return (status);
}
@@ -556,6 +563,12 @@ smb_oplock_req_excl(
ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
/*
* Don't allow grants on closing ofiles.
*/
@@ -1056,6 +1069,12 @@ smb_oplock_req_shared(
ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
/*
* Don't allow grants on closing ofiles.
*/
@@ -1481,8 +1500,8 @@ smb_oplock_ack_break(
boolean_t FoundMatchingRHOplock = B_FALSE;
int other_keys;
- smb_llist_enter(&node->n_ofile_list, RW_READER);
- mutex_enter(&node->n_oplock.ol_mutex);
+ ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
+ ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
/*
* If Open.Stream.Oplock is empty, the operation MUST be
@@ -1594,21 +1613,10 @@ smb_oplock_ack_break(
* OplockCompletionStatus = STATUS_SUCCESS.
* (Because BreakingOplockOpen is equal to the
* passed-in Open, the operation ends at this point.)
- *
- * It should be OK to return the reduced oplock
- * (*rop = LEVEL_NONE) here and avoid the need
- * to send another oplock break. This is safe
- * because we already have an Ack of the break
- * to Level_II, and the additional break to none
- * would use AckRequired = FALSE.
- *
- * If we followed the spec here, we'd have:
- * smb_oplock_ind_break(ofile,
- * LEVEL_NONE, B_FALSE,
- * NT_STATUS_SUCCESS);
- * (Or smb_oplock_ind_break_in_ack...)
*/
- *rop = LEVEL_NONE; /* Reduced from L2 */
+ smb_oplock_ind_break_in_ack(
+ sr, ofile,
+ LEVEL_NONE, B_FALSE);
}
status = NT_STATUS_SUCCESS;
goto out;
@@ -1684,9 +1692,19 @@ smb_oplock_ack_break(
BreakToLevel = READ_CACHING;
break;
case BREAK_TO_NO_CACHING:
- default:
BreakToLevel = LEVEL_NONE;
break;
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case 0:
+ /*
+ * This can happen when we have multiple RH opens,
+ * and one of them breaks (RH to R). Happens in
+ * the smbtorture smb2.lease.v2rename test.
+ */
+ BreakToLevel = CACHE_R;
+ break;
}
/* Switch Open.Stream.Oplock.State */
@@ -1697,6 +1715,21 @@ smb_oplock_ack_break(
case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING):
case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING):
/*
+ * XXX: Missing from [MS-FSA]
+ *
+ * If we previously sent a break to none and the
+ * client Ack level is R instead of none, we
+ * need to send another break. We can then
+ * proceed as if we got level = none.
+ */
+ if (level == CACHE_R && BreakToLevel == LEVEL_NONE) {
+ smb_oplock_ind_break_in_ack(
+ sr, ofile,
+ LEVEL_NONE, B_FALSE);
+ level = LEVEL_NONE;
+ }
+
+ /*
* For each RHOpContext ThisContext in
* Open.Stream.Oplock.RHBreakQueue:
* If ThisContext.Open equals Open:
@@ -1994,7 +2027,11 @@ smb_oplock_ack_break(
*/
/*
- * Breaking R to none? This is like:
+ * Breaking R to none.
+ *
+ * We sent break exclusive (RWH or RW) to none and
+ * the client Ack reduces to R instead of to none.
+ * Need to send another break. This is like:
* "If BreakCacheLevel contains READ_CACHING..."
* from smb_oplock_break_cmn.
*/
@@ -2008,16 +2045,24 @@ smb_oplock_ack_break(
}
/*
- * Breaking RH to R or RH to none? This is like:
+ * Breaking RH to R or RH to none.
+ *
+ * We sent break from (RWH or RW) to (R or none),
+ * and the client Ack reduces to RH instead of none.
+ * Need to send another break. This is like:
* "If BreakCacheLevel equals HANDLE_CACHING..."
* from smb_oplock_break_cmn.
+ *
+ * Note: Windows always does break to CACHE_R here,
+ * letting another Ack and ind_break round trip
+ * take us the rest of the way from R to none.
*/
if (level == CACHE_RH &&
(BreakToLevel == CACHE_R ||
BreakToLevel == LEVEL_NONE)) {
smb_oplock_ind_break_in_ack(
sr, ofile,
- BreakToLevel, B_TRUE);
+ CACHE_R, B_TRUE);
ofile->f_oplock.BreakingToRead =
(BreakToLevel & READ_CACHING) ? 1: 0;
@@ -2069,6 +2114,7 @@ smb_oplock_ack_break(
* the spec for this bit of code. Therefore, this will
* return SUCCESS instead of OPLOCK_BREAK_IN_PROGRESS.
*/
+ ASSERT(node->n_oplock.excl_open == ofile);
node->n_oplock.ol_state = level | EXCLUSIVE;
status = NT_STATUS_SUCCESS;
break; /* case (READ_CACHING|WRITE_CACHING|...) */
@@ -2088,11 +2134,6 @@ out:
type == LEVEL_GRANULAR &&
*rop != LEVEL_NONE) {
*rop |= LEVEL_GRANULAR;
- /* As above, leased oplock may have moved. */
-#ifndef TESTJIG
- if (ofile->f_lease != NULL)
- ofile->f_lease->ls_oplock_ofile = ofile;
-#endif
}
/*
@@ -2111,9 +2152,14 @@ out:
* The spec. describes waiting for a break here,
* but we let the caller do that (when needed) if
* status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
+ *
+ * After some research, smb_oplock_ack_break()
+ * never returns that status. Paranoid check.
*/
- mutex_exit(&node->n_oplock.ol_mutex);
- smb_llist_exit(&node->n_ofile_list);
+ if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ ASSERT(!"Unexpected OPLOCK_BREAK_IN_PROGRESS");
+ status = NT_STATUS_SUCCESS;
+ }
return (status);
}
@@ -2323,6 +2369,12 @@ smb_oplock_break_CLOSE(smb_node_t *node, smb_ofile_t *ofile)
ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
/*
* If Oplock.IIOplocks is not empty:
* For each Open ThisOpen in Oplock.IIOplocks:
@@ -2693,6 +2745,12 @@ smb_oplock_break_cmn(smb_node_t *node,
smb_llist_enter(&node->n_ofile_list, RW_READER);
mutex_enter(&node->n_oplock.ol_mutex);
+#ifdef DEBUG
+ FOREACH_NODE_OFILE(node, o) {
+ DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
+ }
+#endif
+
if (node->n_oplock.ol_state == 0 ||
node->n_oplock.ol_state == NO_OPLOCK)
goto out;
@@ -2751,6 +2809,8 @@ smb_oplock_break_cmn(smb_node_t *node,
* completes some earlier call to
* 2.1.5.17.1.)
*/
+ o = nol->excl_open;
+ ASSERT(o != NULL);
smb_oplock_ind_break(o,
LEVEL_TWO, B_TRUE,
NT_STATUS_SUCCESS);
@@ -2829,6 +2889,8 @@ smb_oplock_break_cmn(smb_node_t *node,
* completes some earlier call to
* 2.1.5.17.1.)
*/
+ o = nol->excl_open;
+ ASSERT(o != NULL);
smb_oplock_ind_break(o,
LEVEL_NONE, B_TRUE,
NT_STATUS_SUCCESS);
@@ -2904,7 +2966,7 @@ smb_oplock_break_cmn(smb_node_t *node,
* equals Open.TargetOplockKey,
* go to the LeaveBreakToNone label.
*/
- if (o != NULL &&
+ if ((o = nol->excl_open) != NULL &&
CompareOplockKeys(ofile, o, CmpFlags))
goto LeaveBreakToNone;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
index b58d3f9f0f..a09d5f80de 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -250,7 +251,6 @@ smb_com_locking_andx(smb_request_t *sr)
DWORD result;
int rc;
uint32_t ltype;
- uint32_t status;
smb_ofile_t *ofile;
uint16_t tmp_pid; /* locking uses 16-bit pids */
uint32_t lrv_tot;
@@ -288,18 +288,16 @@ smb_com_locking_andx(smb_request_t *sr)
ltype = SMB_LOCK_TYPE_READWRITE;
if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
- uint32_t NewLevel;
- if (oplock_level == 0)
- NewLevel = OPLOCK_LEVEL_NONE;
- else
- NewLevel = OPLOCK_LEVEL_TWO;
- status = smb_oplock_ack_break(sr, ofile, &NewLevel);
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = 0;
- }
+ smb1_oplock_ack_break(sr, oplock_level);
if (unlock_num == 0 && lock_num == 0)
return (SDRC_NO_REPLY);
+ /*
+ * Don't allow combining other lock/unlock actions
+ * with an oplock ACK (normally don't get here).
+ */
+ smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
+ ERRDOS, ERROR_INVALID_PARAMETER);
+ return (SDRC_ERROR);
}
/*
@@ -413,57 +411,3 @@ out:
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
-
-/*
- * Compose an SMB1 Oplock Break Notification packet, including
- * the SMB1 header and everything, in sr->reply.
- * The caller will send it and free the request.
- */
-void
-smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
-{
- smb_ofile_t *ofile = sr->fid_ofile;
- uint16_t fid;
- uint8_t lock_type;
- uint8_t oplock_level;
-
- /*
- * Convert internal level to SMB1
- */
- switch (NewLevel) {
- default:
- ASSERT(0);
- /* FALLTHROUGH */
- case OPLOCK_LEVEL_NONE:
- oplock_level = 0;
- break;
-
- case OPLOCK_LEVEL_TWO:
- oplock_level = 1;
- break;
- }
-
- sr->smb_com = SMB_COM_LOCKING_ANDX;
- sr->smb_tid = ofile->f_tree->t_tid;
- sr->smb_pid = 0xFFFF;
- sr->smb_uid = 0;
- sr->smb_mid = 0xFFFF;
- fid = ofile->f_fid;
- lock_type = LOCKING_ANDX_OPLOCK_RELEASE;
-
- (void) smb_mbc_encodef(
- &sr->reply, "Mb19.wwwwbb3.wbb10.",
- /* "\xffSMB" M */
- sr->smb_com, /* b */
- /* status, flags, signature 19. */
- sr->smb_tid, /* w */
- sr->smb_pid, /* w */
- sr->smb_uid, /* w */
- sr->smb_mid, /* w */
- 8, /* word count b */
- 0xFF, /* AndX cmd b */
- /* AndX reserved, offset 3. */
- fid,
- lock_type,
- oplock_level);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index 9279b71981..379006a5e8 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -227,7 +227,7 @@
* Transition T7
*
* This transition occurs in smb_session_durable_timers() and
- * smb_oplock_send_brk(). The ofile will soon be closed.
+ * smb_oplock_send_break(). The ofile will soon be closed.
* In the former case, f_timeout_offset nanoseconds have passed since
* the ofile was orphaned. In the latter, an oplock break occured
* on the ofile while it was orphaned.
@@ -444,6 +444,10 @@ smb_ofile_open(
* SMB_OFILE_STATE_OPEN protocol close, smb_ofile_drop
* SMB_OFILE_STATE_EXPIRED called via smb2_dh_expire
* SMB_OFILE_STATE_ORPHANED smb2_dh_shutdown
+ *
+ * Not that this enters the of->node->n_ofile_list rwlock as reader,
+ * (via smb_oplock_close) so this must not be called while holding
+ * that rwlock.
*/
void
smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
@@ -453,21 +457,7 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
SMB_OFILE_VALID(of);
if (of->f_ftype == SMB_FTYPE_DISK) {
- smb_node_t *node = of->f_node;
-
- smb_llist_enter(&node->n_ofile_list, RW_READER);
- mutex_enter(&node->n_oplock.ol_mutex);
-
- if (of->f_oplock_closing == B_FALSE) {
- of->f_oplock_closing = B_TRUE;
-
- if (of->f_lease != NULL)
- smb2_lease_ofile_close(of);
- smb_oplock_break_CLOSE(node, of);
- }
-
- mutex_exit(&node->n_oplock.ol_mutex);
- smb_llist_exit(&node->n_ofile_list);
+ smb_oplock_close(of);
}
mutex_enter(&of->f_mutex);
@@ -743,7 +733,7 @@ smb_ofile_enum(smb_ofile_t *of, smb_svcenum_t *svcenum)
/*
* Take a reference on an open file, in any of the states:
- * RECONNECT, SAVE_DH, OPEN, ORPHANED.
+ * RECONNECT, SAVE_DH, OPEN, ORPHANED, EXPIRED.
* Return TRUE if ref taken. Used for oplock breaks.
*
* Note: When the oplock break code calls this, it holds the
@@ -774,13 +764,20 @@ again:
goto again;
case SMB_OFILE_STATE_OPEN:
- case SMB_OFILE_STATE_ORPHANED:
case SMB_OFILE_STATE_SAVE_DH:
+ case SMB_OFILE_STATE_ORPHANED:
+ case SMB_OFILE_STATE_EXPIRED:
of->f_refcnt++;
ret = B_TRUE;
break;
+ /*
+ * This is called only when an ofile has an oplock,
+ * so if we come across states that should not have
+ * an oplock, let's debug how that happened.
+ */
default:
+ ASSERT(0);
break;
}
mutex_exit(&of->f_mutex);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
index 5215da8693..58447a98f3 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_oplock.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
/*
@@ -32,6 +33,199 @@
#define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
/*
+ * This is called by the SMB1 "Locking_andX" handler,
+ * for SMB1 oplock break acknowledgement.
+ * This is an "Ack" from the client.
+ */
+void
+smb1_oplock_ack_break(smb_request_t *sr, uchar_t oplock_level)
+{
+ smb_ofile_t *ofile;
+ smb_node_t *node;
+ uint32_t NewLevel;
+
+ ofile = sr->fid_ofile;
+ node = ofile->f_node;
+
+ if (oplock_level == 0)
+ NewLevel = OPLOCK_LEVEL_NONE;
+ else
+ NewLevel = OPLOCK_LEVEL_TWO;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+
+ (void) smb_oplock_ack_break(sr, ofile, &NewLevel);
+
+ ofile->f_oplock.og_state = NewLevel;
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+}
+
+/*
+ * Compose an SMB1 Oplock Break Notification packet, including
+ * the SMB1 header and everything, in sr->reply.
+ * The caller will send it and free the request.
+ */
+static void
+smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ uint16_t fid;
+ uint8_t lock_type;
+ uint8_t oplock_level;
+
+ /*
+ * Convert internal level to SMB1
+ */
+ switch (NewLevel) {
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case OPLOCK_LEVEL_NONE:
+ oplock_level = 0;
+ break;
+
+ case OPLOCK_LEVEL_TWO:
+ oplock_level = 1;
+ break;
+ }
+
+ sr->smb_com = SMB_COM_LOCKING_ANDX;
+ sr->smb_tid = ofile->f_tree->t_tid;
+ sr->smb_pid = 0xFFFF;
+ sr->smb_uid = 0;
+ sr->smb_mid = 0xFFFF;
+ fid = ofile->f_fid;
+ lock_type = LOCKING_ANDX_OPLOCK_RELEASE;
+
+ (void) smb_mbc_encodef(
+ &sr->reply, "Mb19.wwwwbb3.wbb10.",
+ /* "\xffSMB" M */
+ sr->smb_com, /* b */
+ /* status, flags, signature 19. */
+ sr->smb_tid, /* w */
+ sr->smb_pid, /* w */
+ sr->smb_uid, /* w */
+ sr->smb_mid, /* w */
+ 8, /* word count b */
+ 0xFF, /* AndX cmd b */
+ /* AndX reserved, offset 3. */
+ fid,
+ lock_type,
+ oplock_level);
+}
+
+/*
+ * Send an oplock break over the wire, or if we can't,
+ * then process the oplock break locally.
+ *
+ * [MS-CIFS] 3.3.4.2 Object Store Indicates an OpLock Break
+ *
+ * This is mostly similar to smb2_oplock_send_break()
+ * See top comment there about the design.
+ * Called from smb_oplock_async_break.
+ *
+ * This handles only SMB1, which has no durable handles,
+ * and never has GRANULAR oplocks.
+ */
+void
+smb1_oplock_send_break(smb_request_t *sr)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_node_t *node = ofile->f_node;
+ uint32_t NewLevel = sr->arg.olbrk.NewLevel;
+ boolean_t AckReq = sr->arg.olbrk.AckRequired;
+ uint32_t status;
+ int rc;
+
+ /*
+ * SMB1 clients should only get Level II oplocks if they
+ * set the capability indicating they know about them.
+ */
+ if (NewLevel == OPLOCK_LEVEL_TWO &&
+ ofile->f_oplock.og_dialect < NT_LM_0_12)
+ NewLevel = OPLOCK_LEVEL_NONE;
+
+ /*
+ * Build the break message in sr->reply.
+ * It's free'd in smb_request_free().
+ * Always SMB1 here.
+ */
+ sr->reply.max_bytes = MLEN;
+ smb1_oplock_break_notification(sr, NewLevel);
+
+ /*
+ * Try to send the break message to the client.
+ * If connected, this IF body will be true.
+ */
+ if (sr->session == ofile->f_session)
+ rc = smb_session_send(sr->session, 0, &sr->reply);
+ else
+ rc = ENOTCONN;
+
+ if (rc != 0) {
+ /*
+ * We were unable to send the oplock break request,
+ * presumably because the connection is gone.
+ * Just close the handle.
+ */
+ smb_ofile_close(ofile, 0);
+ return;
+ }
+
+ /*
+ * OK, we were able to send the break message.
+ * If no ack. required, we're done.
+ */
+ if (!AckReq)
+ return;
+
+ /*
+ * We're expecting an ACK. Wait in this thread
+ * so we can log clients that don't respond.
+ */
+ status = smb_oplock_wait_ack(sr, NewLevel);
+ if (status == 0)
+ return;
+
+ cmn_err(CE_NOTE, "clnt %s oplock break timeout",
+ sr->session->ip_addr_str);
+ DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
+
+ /*
+ * Did not get an ACK, so do the ACK locally.
+ * Note: always break to none here, regardless
+ * of what the passed in cache level was.
+ */
+ NewLevel = OPLOCK_LEVEL_NONE;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+
+ status = smb_oplock_ack_break(sr, ofile, &NewLevel);
+
+ ofile->f_oplock.og_state = NewLevel;
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+
+#ifdef DEBUG
+ if (status != 0) {
+ cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
+ sr->session->ip_addr_str, status);
+ }
+#endif
+}
+
+/*
* Client has an open handle and requests an oplock.
* Convert SMB1 oplock request info in to internal form,
* call common oplock code, convert result to SMB1.
@@ -96,7 +290,7 @@ smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
}
/*
- * If exclusive failed (or tree forced shared oplocks)
+ * If exclusive failed (or the tree forced shared oplocks)
* and if the caller supports Level II, try shared.
*/
if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
@@ -106,16 +300,7 @@ smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
}
/*
- * Either of the above may have returned the
- * status code that says we should wait.
- */
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
- status = 0;
- }
-
- /*
- * Keep track of what we got (in ofile->f_oplock.og_state)
+ * Keep track of what we got (ofile->f_oplock.og_state etc)
* so we'll know what we had when sending a break later.
* The og_dialect here is the oplock dialect, which may be
* different than SMB dialect. Pre-NT clients did not
@@ -123,27 +308,28 @@ smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
* client that didn't set the CAP_LEVEL_II_OPLOCKS in
* its capabilities, let og_dialect = LANMAN2_1.
*/
- ofile->f_oplock.og_dialect = (level2ok) ?
- NT_LM_0_12 : LANMAN2_1;
switch (status) {
case NT_STATUS_SUCCESS:
- ofile->f_oplock.og_state = op->op_oplock_state;
+ case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
+ ofile->f_oplock.og_dialect = (level2ok) ?
+ NT_LM_0_12 : LANMAN2_1;
+ ofile->f_oplock.og_state = op->op_oplock_state;
+ ofile->f_oplock.og_breakto = op->op_oplock_state;
+ ofile->f_oplock.og_breaking = B_FALSE;
break;
case NT_STATUS_OPLOCK_NOT_GRANTED:
- ofile->f_oplock.og_state = 0;
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
default:
/* Caller did not check args sufficiently? */
cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
sr->session->ip_addr_str, status);
- ofile->f_oplock.og_state = 0;
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
/*
- * Have STATUS_SUCCESS
+ * Only succes cases get here.
* Convert internal oplock state to SMB1
*/
if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
@@ -155,4 +341,12 @@ smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
} else {
op->op_oplock_level = SMB_OPLOCK_NONE;
}
+
+ /*
+ * An smb_oplock_reqest call may have returned the
+ * status code that says we should wait.
+ */
+ if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+ (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
+ }
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
index 76560f20a5..fbb2b51c24 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
@@ -73,7 +73,9 @@ int smb_oplock_timeout_ack = 30000; /* mSec. */
int smb_oplock_timeout_def = 45000; /* mSec. */
static void smb_oplock_async_break(void *);
-static void smb_oplock_hdl_clear(smb_ofile_t *);
+static void smb_oplock_hdl_update(smb_request_t *sr);
+static void smb_oplock_hdl_moved(smb_ofile_t *);
+static void smb_oplock_hdl_closed(smb_ofile_t *);
static void smb_oplock_wait_break_cancel(smb_request_t *sr);
@@ -181,8 +183,7 @@ smb_oplock_ind_break_in_ack(smb_request_t *ack_sr, smb_ofile_t *ofile,
* We're going to schedule a request that will have a
* reference to this ofile. Get the hold first.
*/
- if (ofile->f_oplock_closing ||
- !smb_ofile_hold_olbrk(ofile)) {
+ if (!smb_ofile_hold_olbrk(ofile)) {
/* It's closing (or whatever). Nothing to do. */
return;
}
@@ -226,9 +227,20 @@ smb_oplock_ind_break_in_ack(smb_request_t *ack_sr, smb_ofile_t *ofile,
sr->uid_user = ofile->f_user;
smb_user_hold_internal(sr->uid_user);
}
+ if (ofile->f_lease != NULL)
+ NewLevel |= OPLOCK_LEVEL_GRANULAR;
+
sr->arg.olbrk.NewLevel = NewLevel;
sr->arg.olbrk.AckRequired = AckRequired;
+ /*
+ * Could do this in _hdl_update but this way it's
+ * visible in the dtrace fbt entry probe.
+ */
+ sr->arg.olbrk.OldLevel = ofile->f_oplock.og_breakto;
+
+ smb_oplock_hdl_update(sr);
+
if (use_postwork) {
/*
* Using smb2_cmd_code to indicate what to call.
@@ -253,6 +265,8 @@ smb_oplock_ind_break_in_ack(smb_request_t *ack_sr, smb_ofile_t *ofile,
* Schedule a request & taskq job to do oplock break work
* as requested by the FS-level code (smb_cmn_oplock.c).
*
+ * See also: smb_oplock_ind_break_in_ack
+ *
* Note called with the node ofile list rwlock held and
* the oplock mutex entered.
*/
@@ -281,11 +295,11 @@ smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
break;
case STATUS_NEW_HANDLE:
- /* nothing to do (keep for observability) */
+ smb_oplock_hdl_moved(ofile);
return;
case NT_STATUS_OPLOCK_HANDLE_CLOSED:
- smb_oplock_hdl_clear(ofile);
+ smb_oplock_hdl_closed(ofile);
return;
default:
@@ -297,8 +311,7 @@ smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
* We're going to schedule a request that will have a
* reference to this ofile. Get the hold first.
*/
- if (ofile->f_oplock_closing ||
- !smb_ofile_hold_olbrk(ofile)) {
+ if (!smb_ofile_hold_olbrk(ofile)) {
/* It's closing (or whatever). Nothing to do. */
return;
}
@@ -346,12 +359,23 @@ smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
sr->uid_user = ofile->f_user;
smb_user_hold_internal(sr->uid_user);
}
+ if (ofile->f_lease != NULL)
+ NewLevel |= OPLOCK_LEVEL_GRANULAR;
+
sr->arg.olbrk.NewLevel = NewLevel;
sr->arg.olbrk.AckRequired = AckRequired;
sr->smb2_status = CompletionStatus;
- (void) taskq_dispatch(
- sv->sv_worker_pool,
+ /*
+ * Could do this in _hdl_update but this way it's
+ * visible in the dtrace fbt entry probe.
+ */
+ sr->arg.olbrk.OldLevel = ofile->f_oplock.og_breakto;
+
+ smb_oplock_hdl_update(sr);
+
+ /* Will call smb_oplock_send_break */
+ (void) taskq_dispatch(sv->sv_worker_pool,
smb_oplock_async_break, sr, TQ_SLEEP);
}
@@ -362,16 +386,7 @@ smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
* We have a hold on the ofile, which will be released in
* smb_request_free (via sr->fid_ofile)
*
- * Note we have: sr->uid_user == NULL, sr->tid_tree == NULL.
- * Nothing called here needs those.
- *
- * Note that NewLevel as provided by the FS up-call does NOT
- * include the GRANULAR flag. The SMB level is expected to
- * keep track of how each oplock was acquired (by lease or
- * traditional oplock request) and put the GRANULAR flag
- * back into the oplock state when calling down to the
- * FS-level code. Also note that the lease break message
- * carries only the cache flags, not the GRANULAR flag.
+ * Note we may have: sr->uid_user == NULL, sr->tid_tree == NULL.
*/
static void
smb_oplock_async_break(void *arg)
@@ -398,7 +413,7 @@ smb_oplock_async_break(void *arg)
case STATUS_CANT_GRANT:
case NT_STATUS_SUCCESS:
- smb_oplock_send_brk(sr);
+ smb_oplock_send_break(sr);
break;
default:
@@ -416,275 +431,307 @@ smb_oplock_async_break(void *arg)
smb_request_free(sr);
}
-static void
-smb_oplock_update(smb_request_t *sr, smb_ofile_t *ofile, uint32_t NewLevel)
+/*
+ * Send an oplock (or lease) break to the client.
+ * If we can't, then do a local break.
+ *
+ * This is called either from smb_oplock_async_break via a
+ * taskq job scheduled in smb_oplock_ind_break, or from the
+ * smb2sr_append_postwork() mechanism when we're doing a
+ * "break in ack", via smb_oplock_ind_break_in_ack.
+ *
+ * We don't always have an sr->session here, so
+ * determine the oplock type (lease etc) from
+ * f_lease and f_oplock.og_dialect etc.
+ */
+void
+smb_oplock_send_break(smb_request_t *sr)
{
+ smb_ofile_t *ofile = sr->fid_ofile;
+
if (ofile->f_lease != NULL)
- ofile->f_lease->ls_state = NewLevel & CACHE_RWH;
+ smb2_lease_send_break(sr);
+ else if (ofile->f_oplock.og_dialect >= SMB_VERS_2_BASE)
+ smb2_oplock_send_break(sr);
else
- ofile->f_oplock.og_state = NewLevel;
+ smb1_oplock_send_break(sr);
+}
- if (ofile->dh_persist) {
- smb2_dh_update_oplock(sr, ofile);
+/*
+ * Called by smb_oplock_ind_break for the case STATUS_NEW_HANDLE,
+ * which is an alias for NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
+ *
+ * The FS-level oplock layer calls this to update the SMB-level state
+ * when the oplock for some lease is about to move to a different
+ * ofile on the lease.
+ *
+ * To avoid later confusion, clear og_state on this ofile now.
+ * Without this, smb_oplock_move() may issue debug complaints
+ * about moving oplock state onto a non-empty oplock.
+ */
+static const smb_ofile_t invalid_ofile;
+static void
+smb_oplock_hdl_moved(smb_ofile_t *ofile)
+{
+ smb_lease_t *ls = ofile->f_lease;
+
+ ASSERT(ls != NULL);
+ if (ls != NULL && ls->ls_oplock_ofile == ofile)
+ ls->ls_oplock_ofile = (smb_ofile_t *)&invalid_ofile;
+
+ ofile->f_oplock.og_state = 0;
+ ofile->f_oplock.og_breakto = 0;
+ ofile->f_oplock.og_breaking = B_FALSE;
+}
+
+/*
+ * See: NT_STATUS_OPLOCK_HANDLE_CLOSED above and
+ * smb_ofile_close, smb_oplock_break_CLOSE.
+ *
+ * The FS-level oplock layer calls this to update the
+ * SMB-level state when a handle loses its oplock.
+ */
+static void
+smb_oplock_hdl_closed(smb_ofile_t *ofile)
+{
+ smb_lease_t *lease = ofile->f_lease;
+
+ if (lease != NULL) {
+ if (lease->ls_oplock_ofile == ofile) {
+ /*
+ * smb2_lease_ofile_close should have
+ * moved the oplock to another ofile.
+ */
+ ASSERT(0);
+ lease->ls_oplock_ofile = NULL;
+ }
}
+ ofile->f_oplock.og_state = 0;
+ ofile->f_oplock.og_breakto = 0;
+ ofile->f_oplock.og_breaking = B_FALSE;
}
-#ifdef DEBUG
-int smb_oplock_debug_wait = 0;
+/*
+ * smb_oplock_hdl_update
+ *
+ * Called by smb_oplock_ind_break (and ...in_ack) just before we
+ * schedule smb_oplock_async_break / mb_oplock_send_break taskq job,
+ * so we can make any state changes that should happen immediately.
+ *
+ * Here, keep track of what we will send to the client.
+ * Saves old state in arg.olbck.OldLevel
+ *
+ * Note that because we may be in the midst of processing an
+ * smb_oplock_ack_break call here, the _breaking flag will be
+ * temporarily false, and is set true again if this ack causes
+ * another break. This makes it tricky to know when to update
+ * the epoch, which is not supposed to increment when there's
+ * already an unacknowledged break out to the client.
+ * We can recognize that by comparing ls_state vs ls_breakto.
+ * If no unacknowledged break, ls_state == ls_breakto.
+ */
+static void
+smb_oplock_hdl_update(smb_request_t *sr)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_lease_t *lease = ofile->f_lease;
+ uint32_t NewLevel = sr->arg.olbrk.NewLevel;
+ boolean_t AckReq = sr->arg.olbrk.AckRequired;
+
+#ifdef DEBUG
+ smb_node_t *node = ofile->f_node;
+ ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
+ ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
#endif
+ /* Caller sets arg.olbrk.OldLevel */
+ ofile->f_oplock.og_breakto = NewLevel;
+ ofile->f_oplock.og_breaking = B_TRUE;
+ if (lease != NULL) {
+ // If no unacknowledged break, update epoch.
+ if (lease->ls_breakto == lease->ls_state)
+ lease->ls_epoch++;
+
+ lease->ls_breakto = NewLevel;
+ lease->ls_breaking = B_TRUE;
+ }
+
+ if (!AckReq) {
+ /*
+ * Not expecting an Ack from the client.
+ * Update state immediately.
+ */
+ ofile->f_oplock.og_state = NewLevel;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ if (lease != NULL) {
+ lease->ls_state = NewLevel;
+ lease->ls_breaking = B_FALSE;
+ }
+ if (ofile->dh_persist) {
+ smb2_dh_update_oplock(sr, ofile);
+ }
+ }
+}
+
/*
- * Send an oplock break over the wire, or if we can't,
- * then process the oplock break locally.
- *
- * Note that we have sr->fid_ofile here but all the other
- * normal sr members may be NULL: uid_user, tid_tree.
- * Also sr->session may or may not be the same session as
- * the ofile came from (ofile->f_session) depending on
- * whether this is a "live" open or an orphaned DH,
- * where ofile->f_session will be NULL.
- *
- * Given that we don't always have a session, we determine
- * the oplock type (lease etc) from f_oplock.og_dialect.
+ * Helper for smb_ofile_close
+ *
+ * Note that a client may close an ofile in response to an
+ * oplock break or lease break intead of doing an Ack break,
+ * so this must wake anything that might be waiting on an ack.
*/
void
-smb_oplock_send_brk(smb_request_t *sr)
+smb_oplock_close(smb_ofile_t *ofile)
{
- smb_ofile_t *ofile;
- smb_lease_t *lease;
- uint32_t NewLevel;
- boolean_t AckReq;
- uint32_t status;
- int rc;
-
- ofile = sr->fid_ofile;
- NewLevel = sr->arg.olbrk.NewLevel;
- AckReq = sr->arg.olbrk.AckRequired;
- lease = ofile->f_lease;
+ smb_node_t *node = ofile->f_node;
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ mutex_enter(&node->n_oplock.ol_mutex);
+
+ if (ofile->f_oplock_closing == B_FALSE) {
+ ofile->f_oplock_closing = B_TRUE;
+
+ if (ofile->f_lease != NULL)
+ smb2_lease_ofile_close(ofile);
+
+ smb_oplock_break_CLOSE(node, ofile);
+
+ ofile->f_oplock.og_state = 0;
+ ofile->f_oplock.og_breakto = 0;
+ ofile->f_oplock.og_breaking = B_FALSE;
+ cv_broadcast(&ofile->f_oplock.og_ack_cv);
+ }
+
+ mutex_exit(&node->n_oplock.ol_mutex);
+ smb_llist_exit(&node->n_ofile_list);
+}
+
+/*
+ * Called by smb_request_cancel() via sr->cancel_method
+ * Arg is the smb_node_t with the breaking oplock.
+ */
+static void
+smb_oplock_wait_ack_cancel(smb_request_t *sr)
+{
+ kcondvar_t *cvp = sr->cancel_arg2;
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_node_t *node = ofile->f_node;
+
+ mutex_enter(&node->n_oplock.ol_mutex);
+ cv_broadcast(cvp);
+ mutex_exit(&node->n_oplock.ol_mutex);
+}
+
+/*
+ * Wait for an oplock break ACK to arrive. This is called after
+ * we've sent an oplock break or lease break to the client where
+ * an "Ack break" is expected back. If we get an Ack, that will
+ * wake us up via smb2_oplock_break_ack or smb2_lease_break_ack.
+ *
+ * Wait until state reduced to NewLevel (or less).
+ * Note that in multi-break cases, we might wait here for just
+ * one ack when another has become pending, in which case the
+ * og_breakto might be a subset of NewLevel. Wait until the
+ * state field is no longer a superset of NewLevel.
+ */
+uint32_t
+smb_oplock_wait_ack(smb_request_t *sr, uint32_t NewLevel)
+{
+ smb_ofile_t *ofile = sr->fid_ofile;
+ smb_lease_t *lease = ofile->f_lease;
+ smb_node_t *node = ofile->f_node;
+ smb_oplock_t *ol = &node->n_oplock;
+ uint32_t *state_p;
+ kcondvar_t *cv_p;
+ clock_t time, rv;
+ uint32_t status = 0;
+ smb_req_state_t srstate;
+ uint32_t wait_mask;
+
+ time = ddi_get_lbolt() +
+ MSEC_TO_TICK(smb_oplock_timeout_ack);
/*
- * Build the break message in sr->reply.
- * It's free'd in smb_request_free().
- * Also updates the lease and NewLevel.
+ * Wait on either lease state or oplock state
*/
- sr->reply.max_bytes = MLEN;
if (lease != NULL) {
- /*
- * The ofile has as lease. Must be SMB2+
- * Oplock state has changed, so update the epoch.
- */
- mutex_enter(&lease->ls_mutex);
- lease->ls_epoch++;
- mutex_exit(&lease->ls_mutex);
-
- /* Note, needs "old" state in ls_state */
- smb2_lease_break_notification(sr,
- (NewLevel & CACHE_RWH), AckReq);
- NewLevel |= OPLOCK_LEVEL_GRANULAR;
- } else if (ofile->f_oplock.og_dialect >= SMB_VERS_2_BASE) {
- /*
- * SMB2 using old-style oplock (no lease)
- */
- smb2_oplock_break_notification(sr, NewLevel);
+ state_p = &lease->ls_state;
+ cv_p = &lease->ls_ack_cv;
} else {
- /*
- * SMB1 clients should only get Level II oplocks if they
- * set the capability indicating they know about them.
- */
- if (NewLevel == OPLOCK_LEVEL_TWO &&
- ofile->f_oplock.og_dialect < NT_LM_0_12)
- NewLevel = OPLOCK_LEVEL_NONE;
- smb1_oplock_break_notification(sr, NewLevel);
+ state_p = &ofile->f_oplock.og_state;
+ cv_p = &ofile->f_oplock.og_ack_cv;
}
/*
- * Keep track of what we last sent to the client,
- * preserving the GRANULAR flag (if a lease).
- * If we're expecting an ACK, set og_breaking
- * (or maybe lease->ls_breaking) so we can
- * filter unsolicited ACKs.
+ * These are all the bits that we wait to be cleared.
*/
- if (AckReq) {
- uint32_t BreakTo;
+ wait_mask = ~NewLevel & (CACHE_RWH |
+ LEVEL_TWO | LEVEL_ONE | LEVEL_BATCH);
- if (lease != NULL) {
- BreakTo = (NewLevel & CACHE_RWH) << BREAK_SHIFT;
- if (BreakTo == 0)
- BreakTo = BREAK_TO_NO_CACHING;
- lease->ls_breaking = BreakTo;
- } else {
- if ((NewLevel & LEVEL_TWO_OPLOCK) != 0)
- BreakTo = BREAK_TO_TWO;
- else
- BreakTo = BREAK_TO_NONE;
- ofile->f_oplock.og_breaking = BreakTo;
- }
- /* Will update ls/og_state in ack. */
- } else {
- smb_oplock_update(sr, ofile, NewLevel);
+ /*
+ * Setup cancellation callback
+ */
+ mutex_enter(&sr->sr_mutex);
+ if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
+ mutex_exit(&sr->sr_mutex);
+ return (NT_STATUS_CANCELLED);
}
+ sr->sr_state = SMB_REQ_STATE_WAITING_OLBRK;
+ sr->cancel_method = smb_oplock_wait_ack_cancel;
+ sr->cancel_arg2 = cv_p;
+ mutex_exit(&sr->sr_mutex);
/*
- * Try to send the break message to the client.
- * When we get to multi-channel, this is supposed to
- * try to send on every channel before giving up.
+ * Enter the wait loop
*/
- if (sr->session == ofile->f_session)
- rc = smb_session_send(sr->session, 0, &sr->reply);
- else
- rc = ENOTCONN;
-
- if (rc == 0) {
- /*
- * OK, we were able to send the break message.
- * If no ack. required, we're done.
- */
- if (!AckReq)
- return;
+ mutex_enter(&ol->ol_mutex);
- /*
- * We're expecting an ACK. Wait in this thread
- * so we can log clients that don't respond.
- *
- * If debugging, may want to break after a
- * short wait to look into why we might be
- * holding up progress. (i.e. locks?)
- */
-#ifdef DEBUG
- if (smb_oplock_debug_wait > 0) {
- status = smb_oplock_wait_break(sr, ofile->f_node,
- smb_oplock_debug_wait);
- if (status == 0)
- return;
- cmn_err(CE_NOTE, "clnt %s oplock break wait debug",
- sr->session->ip_addr_str);
- debug_enter("oplock_wait");
+ while ((*state_p & wait_mask) != 0) {
+ rv = cv_timedwait(cv_p, &ol->ol_mutex, time);
+ if (rv < 0) {
+ /* cv_timewait timeout */
+ status = NT_STATUS_CANNOT_BREAK_OPLOCK;
+ break;
}
-#endif
- status = smb_oplock_wait_break(sr, ofile->f_node,
- smb_oplock_timeout_ack);
- if (status == 0)
- return;
-
- cmn_err(CE_NOTE, "clnt %s oplock break timeout",
- sr->session->ip_addr_str);
- DTRACE_PROBE1(break_timeout, smb_ofile_t *, ofile);
-
- /*
- * Will do local ack below. Note, after timeout,
- * do a break to none or "no caching" regardless
- * of what the passed in cache level was.
- * That means: clear all except GRANULAR.
- */
- NewLevel &= OPLOCK_LEVEL_GRANULAR;
- } else {
- /*
- * We were unable to send the oplock break request.
- * Generally, that means we have no connection to this
- * client right now, and this ofile will have state
- * SMB_OFILE_STATE_ORPHANED. We either close the handle
- * or break the oplock locally, in which case the client
- * gets the updated oplock state when they reconnect.
- * Decide whether to keep or close.
- *
- * Relevant [MS-SMB2] sections:
- *
- * 3.3.4.6 Object Store Indicates an Oplock Break
- * If Open.Connection is NULL, Open.IsResilient is FALSE,
- * Open.IsDurable is FALSE and Open.IsPersistent is FALSE,
- * the server SHOULD close the Open as specified in...
- *
- * 3.3.4.7 Object Store Indicates a Lease Break
- * If Open.Connection is NULL, the server MUST close the
- * Open as specified in ... for the following cases:
- * - Open.IsResilient is FALSE, Open.IsDurable is FALSE,
- * and Open.IsPersistent is FALSE.
- * - Lease.BreakToLeaseState does not contain
- * ...HANDLE_CACHING and Open.IsDurable is TRUE.
- * If Lease.LeaseOpens is empty, (... local ack to "none").
- */
/*
- * See similar logic in smb_dh_should_save
+ * Check if we were woken by smb_request_cancel,
+ * which sets state SMB_REQ_STATE_CANCEL_PENDING
+ * and signals the CV. The mutex enter/exit is
+ * just to ensure cache visibility of sr_state
+ * that was updated in smb_request_cancel.
*/
- switch (ofile->dh_vers) {
- case SMB2_RESILIENT:
- break; /* keep DH */
-
- case SMB2_DURABLE_V2:
- if (ofile->dh_persist)
- break; /* keep DH */
- /* FALLTHROUGH */
- case SMB2_DURABLE_V1:
- /* IS durable (v1 or v2) */
- if ((NewLevel & (OPLOCK_LEVEL_BATCH |
- OPLOCK_LEVEL_CACHE_HANDLE)) != 0)
- break; /* keep DH */
- /* FALLTHROUGH */
- case SMB2_NOT_DURABLE:
- default:
- smb_ofile_close(ofile, 0);
- return;
- }
- /* Keep this ofile (durable handle). */
-
- if (!AckReq) {
- /* Nothing more to do. */
- return;
+ mutex_enter(&sr->sr_mutex);
+ srstate = sr->sr_state;
+ mutex_exit(&sr->sr_mutex);
+ if (srstate != SMB_REQ_STATE_WAITING_OLBRK) {
+ break;
}
}
+ mutex_exit(&ol->ol_mutex);
/*
- * We get here after either an oplock break ack timeout,
- * or a send failure for a durable handle type that we
- * preserve rather than just close. Do local ack.
+ * Clear cancellation callback and see if it fired.
*/
- if (lease != NULL)
- lease->ls_breaking = 0;
- else
- ofile->f_oplock.og_breaking = 0;
-
- status = smb_oplock_ack_break(sr, ofile, &NewLevel);
- if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
- /* Not expecting this status return. */
- cmn_err(CE_NOTE, "clnt local oplock ack wait?");
- (void) smb_oplock_wait_break(sr, ofile->f_node,
- smb_oplock_timeout_ack);
- status = 0;
- }
- if (status != 0) {
- cmn_err(CE_NOTE, "clnt local oplock ack, "
- "status=0x%x", status);
+ mutex_enter(&sr->sr_mutex);
+ sr->cancel_method = NULL;
+ sr->cancel_arg2 = NULL;
+ switch (sr->sr_state) {
+ case SMB_REQ_STATE_WAITING_OLBRK:
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ /* status from above */
+ break;
+ case SMB_REQ_STATE_CANCEL_PENDING:
+ sr->sr_state = SMB_REQ_STATE_CANCELLED;
+ status = NT_STATUS_CANCELLED;
+ break;
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
}
+ mutex_exit(&sr->sr_mutex);
- /* Update ls/og_state as if we heard from the client. */
- smb_oplock_update(sr, ofile, NewLevel);
-}
-
-/*
- * See: NT_STATUS_OPLOCK_HANDLE_CLOSED above and
- * smb_ofile_close, smb_oplock_break_CLOSE.
- *
- * The FS-level oplock layer calls this to update the
- * SMB-level state when a handle loses its oplock.
- */
-static void
-smb_oplock_hdl_clear(smb_ofile_t *ofile)
-{
- smb_lease_t *lease = ofile->f_lease;
-
- if (lease != NULL) {
- if (lease->ls_oplock_ofile == ofile) {
- /*
- * smb2_lease_ofile_close should have
- * moved the oplock to another ofile.
- */
- ASSERT(0);
- lease->ls_oplock_ofile = NULL;
- }
- }
- ofile->f_oplock.og_state = 0;
- ofile->f_oplock.og_breaking = 0;
+ return (status);
}
/*
@@ -760,7 +807,9 @@ smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout)
/*
* Check if we were woken by smb_request_cancel,
* which sets state SMB_REQ_STATE_CANCEL_PENDING
- * and signals WaitingOpenCV.
+ * and signals the CV. The mutex enter/exit is
+ * just to ensure cache visibility of sr_state
+ * that was updated in smb_request_cancel.
*/
mutex_enter(&sr->sr_mutex);
srstate = sr->sr_state;
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index 3ffc10f15e..4a231051ff 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -267,6 +267,7 @@ int smb_net_id(uint32_t);
* Common oplock functions
*/
uint32_t smb_oplock_request(smb_request_t *, smb_ofile_t *, uint32_t *);
+uint32_t smb_oplock_request_LH(smb_request_t *, smb_ofile_t *, uint32_t *);
uint32_t smb_oplock_ack_break(smb_request_t *, smb_ofile_t *, uint32_t *);
uint32_t smb_oplock_break_PARENT(smb_node_t *, smb_ofile_t *);
uint32_t smb_oplock_break_OPEN(smb_node_t *, smb_ofile_t *,
@@ -280,6 +281,16 @@ uint32_t smb_oplock_break_WRITE(smb_node_t *, smb_ofile_t *);
uint32_t smb_oplock_break_SETINFO(smb_node_t *,
smb_ofile_t *ofile, uint32_t InfoClass);
uint32_t smb_oplock_break_DELETE(smb_node_t *, smb_ofile_t *);
+void smb_oplock_close(smb_ofile_t *);
+
+void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t);
+void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *,
+ uint32_t, boolean_t);
+void smb_oplock_send_break(smb_request_t *);
+
+uint32_t smb_oplock_wait_ack(smb_request_t *, uint32_t);
+uint32_t smb_oplock_wait_break(smb_request_t *, smb_node_t *, int);
+uint32_t smb_oplock_wait_break_fem(smb_node_t *, int);
void smb_oplock_move(smb_node_t *, smb_ofile_t *, smb_ofile_t *);
@@ -287,16 +298,12 @@ void smb_oplock_move(smb_node_t *, smb_ofile_t *, smb_ofile_t *);
* Protocol-specific oplock functions
* (and "server-level" functions)
*/
+void smb1_oplock_ack_break(smb_request_t *, uchar_t);
void smb1_oplock_acquire(smb_request_t *, boolean_t);
-void smb1_oplock_break_notification(smb_request_t *, uint32_t);
-void smb2_oplock_break_notification(smb_request_t *, uint32_t);
-void smb2_lease_break_notification(smb_request_t *, uint32_t, boolean_t);
-void smb_oplock_ind_break(smb_ofile_t *, uint32_t, boolean_t, uint32_t);
-void smb_oplock_ind_break_in_ack(smb_request_t *, smb_ofile_t *,
- uint32_t, boolean_t);
-void smb_oplock_send_brk(smb_request_t *);
-uint32_t smb_oplock_wait_break(smb_request_t *, smb_node_t *, int);
-uint32_t smb_oplock_wait_break_fem(smb_node_t *, int);
+void smb1_oplock_send_break(smb_request_t *);
+void smb2_oplock_send_break(smb_request_t *);
+void smb2_lease_ofile_close(smb_ofile_t *);
+void smb2_lease_send_break(smb_request_t *);
/*
* range lock functions - node operations
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index f29a5b24c0..f871fe76b2 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -598,9 +598,11 @@ typedef struct smb_oplock {
*/
typedef struct smb_oplock_grant {
/* smb protocol-level state */
- uint32_t og_state; /* latest sent to client */
- uint32_t og_breaking; /* BREAK_TO... flags */
+ uint32_t og_state; /* what client has now */
+ uint32_t og_breakto; /* level breaking to */
+ boolean_t og_breaking;
uint16_t og_dialect; /* how to send breaks */
+ kcondvar_t og_ack_cv; /* Wait for ACK */
/* File-system level state */
uint8_t onlist_II;
uint8_t onlist_R;
@@ -613,7 +615,7 @@ typedef struct smb_oplock_grant {
typedef struct smb_lease {
list_node_t ls_lnd; /* sv_lease_ht */
- kmutex_t ls_mutex;
+ kmutex_t ls_mutex; /* for ls_refcnt */
smb_llist_t *ls_bucket;
struct smb_node *ls_node;
/*
@@ -622,10 +624,12 @@ typedef struct smb_lease {
*/
void *ls_oplock_ofile;
uint32_t ls_refcnt;
- uint32_t ls_state;
- uint32_t ls_breaking; /* BREAK_TO... flags */
+ uint32_t ls_state; /* what client has now */
+ uint32_t ls_breakto; /* level breaking to */
uint16_t ls_epoch;
uint16_t ls_version;
+ boolean_t ls_breaking;
+ kcondvar_t ls_ack_cv; /* Wait for ACK */
uint8_t ls_key[SMB_LEASE_KEY_SZ];
uint8_t ls_clnt[SMB_LEASE_KEY_SZ];
} smb_lease_t;
@@ -1657,7 +1661,9 @@ typedef struct smb_arg_lock {
typedef struct smb_arg_olbrk {
uint32_t NewLevel;
+ uint32_t OldLevel;
boolean_t AckRequired;
+ uint8_t LeaseKey[SMB_LEASE_KEY_SZ];
} smb_arg_olbrk_t;
/*