diff options
author | Pavel Shilovsky <pshilovsky@etersoft.ru> | 2012-09-19 09:22:43 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:33 -0400 |
commit | f7ba7fe685bc3ed8fd0687870e68b2567d17357f (patch) | |
tree | 364ff08cf616cc740467d44d5026a05ce9d0c33c /fs/cifs | |
parent | 027e8eec31d8141a6231f772e10ccae60c9d5c13 (diff) |
CIFS: Add brlock support for SMB2
Signed-off-by: Pavel Shilovsky <pshilovsky@etersoft.ru>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsproto.h | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 8 | ||||
-rw-r--r-- | fs/cifs/smb2file.c | 97 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 13 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 59 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 24 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 10 |
7 files changed, 210 insertions, 5 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index a7e238f88898..15e38dc389fc 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -190,6 +190,10 @@ extern void cifs_dfs_release_automount_timer(void); | |||
190 | void cifs_proc_init(void); | 190 | void cifs_proc_init(void); |
191 | void cifs_proc_clean(void); | 191 | void cifs_proc_clean(void); |
192 | 192 | ||
193 | extern void cifs_move_llist(struct list_head *source, struct list_head *dest); | ||
194 | extern void cifs_free_llist(struct list_head *llist); | ||
195 | extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); | ||
196 | |||
193 | extern int cifs_negotiate_protocol(const unsigned int xid, | 197 | extern int cifs_negotiate_protocol(const unsigned int xid, |
194 | struct cifs_ses *ses); | 198 | struct cifs_ses *ses); |
195 | extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, | 199 | extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 90ab83647b82..e2a8e4456275 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -288,8 +288,6 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
288 | return cfile; | 288 | return cfile; |
289 | } | 289 | } |
290 | 290 | ||
291 | static void cifs_del_lock_waiters(struct cifsLockInfo *lock); | ||
292 | |||
293 | struct cifsFileInfo * | 291 | struct cifsFileInfo * |
294 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) | 292 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) |
295 | { | 293 | { |
@@ -696,7 +694,7 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type) | |||
696 | return lock; | 694 | return lock; |
697 | } | 695 | } |
698 | 696 | ||
699 | static void | 697 | void |
700 | cifs_del_lock_waiters(struct cifsLockInfo *lock) | 698 | cifs_del_lock_waiters(struct cifsLockInfo *lock) |
701 | { | 699 | { |
702 | struct cifsLockInfo *li, *tmp; | 700 | struct cifsLockInfo *li, *tmp; |
@@ -1229,7 +1227,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, | |||
1229 | return 0; | 1227 | return 0; |
1230 | } | 1228 | } |
1231 | 1229 | ||
1232 | static void | 1230 | void |
1233 | cifs_move_llist(struct list_head *source, struct list_head *dest) | 1231 | cifs_move_llist(struct list_head *source, struct list_head *dest) |
1234 | { | 1232 | { |
1235 | struct list_head *li, *tmp; | 1233 | struct list_head *li, *tmp; |
@@ -1237,7 +1235,7 @@ cifs_move_llist(struct list_head *source, struct list_head *dest) | |||
1237 | list_move(li, dest); | 1235 | list_move(li, dest); |
1238 | } | 1236 | } |
1239 | 1237 | ||
1240 | static void | 1238 | void |
1241 | cifs_free_llist(struct list_head *llist) | 1239 | cifs_free_llist(struct list_head *llist) |
1242 | { | 1240 | { |
1243 | struct cifsLockInfo *li, *tmp; | 1241 | struct cifsLockInfo *li, *tmp; |
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 5ff25e025215..a25ea02149e7 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c | |||
@@ -104,3 +104,100 @@ out: | |||
104 | kfree(smb2_path); | 104 | kfree(smb2_path); |
105 | return rc; | 105 | return rc; |
106 | } | 106 | } |
107 | |||
108 | int | ||
109 | smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, | ||
110 | const unsigned int xid) | ||
111 | { | ||
112 | int rc = 0, stored_rc; | ||
113 | unsigned int max_num, num = 0, max_buf; | ||
114 | struct smb2_lock_element *buf, *cur; | ||
115 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | ||
116 | struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); | ||
117 | struct cifsLockInfo *li, *tmp; | ||
118 | __u64 length = 1 + flock->fl_end - flock->fl_start; | ||
119 | struct list_head tmp_llist; | ||
120 | |||
121 | INIT_LIST_HEAD(&tmp_llist); | ||
122 | |||
123 | /* | ||
124 | * Accessing maxBuf is racy with cifs_reconnect - need to store value | ||
125 | * and check it for zero before using. | ||
126 | */ | ||
127 | max_buf = tcon->ses->server->maxBuf; | ||
128 | if (!max_buf) | ||
129 | return -EINVAL; | ||
130 | |||
131 | max_num = max_buf / sizeof(struct smb2_lock_element); | ||
132 | buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL); | ||
133 | if (!buf) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | cur = buf; | ||
137 | |||
138 | mutex_lock(&cinode->lock_mutex); | ||
139 | list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { | ||
140 | if (flock->fl_start > li->offset || | ||
141 | (flock->fl_start + length) < | ||
142 | (li->offset + li->length)) | ||
143 | continue; | ||
144 | if (current->tgid != li->pid) | ||
145 | continue; | ||
146 | if (cinode->can_cache_brlcks) { | ||
147 | /* | ||
148 | * We can cache brlock requests - simply remove a lock | ||
149 | * from the file's list. | ||
150 | */ | ||
151 | list_del(&li->llist); | ||
152 | cifs_del_lock_waiters(li); | ||
153 | kfree(li); | ||
154 | continue; | ||
155 | } | ||
156 | cur->Length = cpu_to_le64(li->length); | ||
157 | cur->Offset = cpu_to_le64(li->offset); | ||
158 | cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); | ||
159 | /* | ||
160 | * We need to save a lock here to let us add it again to the | ||
161 | * file's list if the unlock range request fails on the server. | ||
162 | */ | ||
163 | list_move(&li->llist, &tmp_llist); | ||
164 | if (++num == max_num) { | ||
165 | stored_rc = smb2_lockv(xid, tcon, | ||
166 | cfile->fid.persistent_fid, | ||
167 | cfile->fid.volatile_fid, | ||
168 | current->tgid, num, buf); | ||
169 | if (stored_rc) { | ||
170 | /* | ||
171 | * We failed on the unlock range request - add | ||
172 | * all locks from the tmp list to the head of | ||
173 | * the file's list. | ||
174 | */ | ||
175 | cifs_move_llist(&tmp_llist, | ||
176 | &cfile->llist->locks); | ||
177 | rc = stored_rc; | ||
178 | } else | ||
179 | /* | ||
180 | * The unlock range request succeed - free the | ||
181 | * tmp list. | ||
182 | */ | ||
183 | cifs_free_llist(&tmp_llist); | ||
184 | cur = buf; | ||
185 | num = 0; | ||
186 | } else | ||
187 | cur++; | ||
188 | } | ||
189 | if (num) { | ||
190 | stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, | ||
191 | cfile->fid.volatile_fid, current->tgid, | ||
192 | num, buf); | ||
193 | if (stored_rc) { | ||
194 | cifs_move_llist(&tmp_llist, &cfile->llist->locks); | ||
195 | rc = stored_rc; | ||
196 | } else | ||
197 | cifs_free_llist(&tmp_llist); | ||
198 | } | ||
199 | mutex_unlock(&cinode->lock_mutex); | ||
200 | |||
201 | kfree(buf); | ||
202 | return rc; | ||
203 | } | ||
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e4a59d1f06b1..caed2c57896d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -544,6 +544,17 @@ smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) | |||
544 | ob1->fid.volatile_fid == ob2->fid.volatile_fid; | 544 | ob1->fid.volatile_fid == ob2->fid.volatile_fid; |
545 | } | 545 | } |
546 | 546 | ||
547 | static int | ||
548 | smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, | ||
549 | __u64 length, __u32 type, int lock, int unlock, bool wait) | ||
550 | { | ||
551 | if (unlock && !lock) | ||
552 | type = SMB2_LOCKFLAG_UNLOCK; | ||
553 | return SMB2_lock(xid, tlink_tcon(cfile->tlink), | ||
554 | cfile->fid.persistent_fid, cfile->fid.volatile_fid, | ||
555 | current->tgid, length, offset, type, wait); | ||
556 | } | ||
557 | |||
547 | struct smb_version_operations smb21_operations = { | 558 | struct smb_version_operations smb21_operations = { |
548 | .compare_fids = smb2_compare_fids, | 559 | .compare_fids = smb2_compare_fids, |
549 | .setup_request = smb2_setup_request, | 560 | .setup_request = smb2_setup_request, |
@@ -602,6 +613,8 @@ struct smb_version_operations smb21_operations = { | |||
602 | .is_status_pending = smb2_is_status_pending, | 613 | .is_status_pending = smb2_is_status_pending, |
603 | .oplock_response = smb2_oplock_response, | 614 | .oplock_response = smb2_oplock_response, |
604 | .queryfs = smb2_queryfs, | 615 | .queryfs = smb2_queryfs, |
616 | .mand_lock = smb2_mand_lock, | ||
617 | .mand_unlock_range = smb2_unlock_range, | ||
605 | }; | 618 | }; |
606 | 619 | ||
607 | struct smb_version_values smb21_values = { | 620 | struct smb_version_values smb21_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1b447612200e..d3e1cfca3379 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -2047,3 +2047,62 @@ qinf_exit: | |||
2047 | free_rsp_buf(resp_buftype, iov.iov_base); | 2047 | free_rsp_buf(resp_buftype, iov.iov_base); |
2048 | return rc; | 2048 | return rc; |
2049 | } | 2049 | } |
2050 | |||
2051 | int | ||
2052 | smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, | ||
2053 | const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, | ||
2054 | const __u32 num_lock, struct smb2_lock_element *buf) | ||
2055 | { | ||
2056 | int rc = 0; | ||
2057 | struct smb2_lock_req *req = NULL; | ||
2058 | struct kvec iov[2]; | ||
2059 | int resp_buf_type; | ||
2060 | unsigned int count; | ||
2061 | |||
2062 | cFYI(1, "smb2_lockv num lock %d", num_lock); | ||
2063 | |||
2064 | rc = small_smb2_init(SMB2_LOCK, tcon, (void **) &req); | ||
2065 | if (rc) | ||
2066 | return rc; | ||
2067 | |||
2068 | req->hdr.ProcessId = cpu_to_le32(pid); | ||
2069 | req->LockCount = cpu_to_le16(num_lock); | ||
2070 | |||
2071 | req->PersistentFileId = persist_fid; | ||
2072 | req->VolatileFileId = volatile_fid; | ||
2073 | |||
2074 | count = num_lock * sizeof(struct smb2_lock_element); | ||
2075 | inc_rfc1001_len(req, count - sizeof(struct smb2_lock_element)); | ||
2076 | |||
2077 | iov[0].iov_base = (char *)req; | ||
2078 | /* 4 for rfc1002 length field and count for all locks */ | ||
2079 | iov[0].iov_len = get_rfc1002_length(req) + 4 - count; | ||
2080 | iov[1].iov_base = (char *)buf; | ||
2081 | iov[1].iov_len = count; | ||
2082 | |||
2083 | cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); | ||
2084 | rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP); | ||
2085 | if (rc) { | ||
2086 | cFYI(1, "Send error in smb2_lockv = %d", rc); | ||
2087 | cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); | ||
2088 | } | ||
2089 | |||
2090 | return rc; | ||
2091 | } | ||
2092 | |||
2093 | int | ||
2094 | SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, | ||
2095 | const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, | ||
2096 | const __u64 length, const __u64 offset, const __u32 lock_flags, | ||
2097 | const bool wait) | ||
2098 | { | ||
2099 | struct smb2_lock_element lock; | ||
2100 | |||
2101 | lock.Offset = cpu_to_le64(offset); | ||
2102 | lock.Length = cpu_to_le64(length); | ||
2103 | lock.Flags = cpu_to_le32(lock_flags); | ||
2104 | if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) | ||
2105 | lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); | ||
2106 | |||
2107 | return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); | ||
2108 | } | ||
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index d2d132e94155..889ee5e193d9 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -531,6 +531,30 @@ struct smb2_write_rsp { | |||
531 | #define SMB2_LOCKFLAG_UNLOCK 0x0004 | 531 | #define SMB2_LOCKFLAG_UNLOCK 0x0004 |
532 | #define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 | 532 | #define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 |
533 | 533 | ||
534 | struct smb2_lock_element { | ||
535 | __le64 Offset; | ||
536 | __le64 Length; | ||
537 | __le32 Flags; | ||
538 | __le32 Reserved; | ||
539 | } __packed; | ||
540 | |||
541 | struct smb2_lock_req { | ||
542 | struct smb2_hdr hdr; | ||
543 | __le16 StructureSize; /* Must be 48 */ | ||
544 | __le16 LockCount; | ||
545 | __le32 Reserved; | ||
546 | __u64 PersistentFileId; /* opaque endianness */ | ||
547 | __u64 VolatileFileId; /* opaque endianness */ | ||
548 | /* Followed by at least one */ | ||
549 | struct smb2_lock_element locks[1]; | ||
550 | } __packed; | ||
551 | |||
552 | struct smb2_lock_rsp { | ||
553 | struct smb2_hdr hdr; | ||
554 | __le16 StructureSize; /* Must be 4 */ | ||
555 | __le16 Reserved; | ||
556 | } __packed; | ||
557 | |||
534 | struct smb2_echo_req { | 558 | struct smb2_echo_req { |
535 | struct smb2_hdr hdr; | 559 | struct smb2_hdr hdr; |
536 | __le16 StructureSize; /* Must be 4 */ | 560 | __le16 StructureSize; /* Must be 4 */ |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index aeb30dbdf8b8..ab19152b092b 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -84,6 +84,8 @@ extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, | |||
84 | struct cifs_fid *fid, __u32 *oplock, | 84 | struct cifs_fid *fid, __u32 *oplock, |
85 | FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb); | 85 | FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb); |
86 | extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); | 86 | extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); |
87 | extern int smb2_unlock_range(struct cifsFileInfo *cfile, | ||
88 | struct file_lock *flock, const unsigned int xid); | ||
87 | 89 | ||
88 | /* | 90 | /* |
89 | * SMB2 Worker functions - most of protocol specific implementation details | 91 | * SMB2 Worker functions - most of protocol specific implementation details |
@@ -140,5 +142,13 @@ extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, | |||
140 | extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, | 142 | extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, |
141 | u64 persistent_file_id, u64 volatile_file_id, | 143 | u64 persistent_file_id, u64 volatile_file_id, |
142 | struct kstatfs *FSData); | 144 | struct kstatfs *FSData); |
145 | extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, | ||
146 | const __u64 persist_fid, const __u64 volatile_fid, | ||
147 | const __u32 pid, const __u64 length, const __u64 offset, | ||
148 | const __u32 lockFlags, const bool wait); | ||
149 | extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, | ||
150 | const __u64 persist_fid, const __u64 volatile_fid, | ||
151 | const __u32 pid, const __u32 num_lock, | ||
152 | struct smb2_lock_element *buf); | ||
143 | 153 | ||
144 | #endif /* _SMB2PROTO_H */ | 154 | #endif /* _SMB2PROTO_H */ |