pvfs_oplock: auto release oplocks after a timeout
[samba.git] / source4 / ntvfs / posix / pvfs_oplock.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - oplock handling
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/messaging/messaging.h"
24 #include "lib/messaging/irpc.h"
25 #include "system/time.h"
26 #include "vfs_posix.h"
27
28
29 struct pvfs_oplock {
30         struct pvfs_file_handle *handle;
31         struct pvfs_file *file;
32         uint32_t level;
33         struct timeval break_to_level_II;
34         struct timeval break_to_none;
35         struct messaging_context *msg_ctx;
36 };
37
38 static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
39                                              uint8_t oplock_break)
40 {
41         struct odb_lock *olck;
42         NTSTATUS status;
43
44         if (h->fd == -1) {
45                 return NT_STATUS_FILE_IS_A_DIRECTORY;
46         }
47
48         if (!h->have_opendb_entry) {
49                 return NT_STATUS_FOOBAR;
50         }
51
52         if (!h->oplock) {
53                 return NT_STATUS_FOOBAR;
54         }
55
56         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
57         if (olck == NULL) {
58                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
59                 return NT_STATUS_FOOBAR;
60         }
61
62         if (oplock_break == OPLOCK_BREAK_TO_NONE) {
63                 h->oplock->level = OPLOCK_NONE;
64         } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
65                 h->oplock->level = OPLOCK_LEVEL_II;
66         } else {
67                 /* fallback to level II in case of a invalid value */
68                 DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
69                 h->oplock->level = OPLOCK_LEVEL_II;
70         }
71         status = odb_update_oplock(olck, h, h->oplock->level);
72         if (!NT_STATUS_IS_OK(status)) {
73                 DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
74                          h->name->full_name, nt_errstr(status)));
75                 talloc_free(olck);
76                 return status;
77         }
78
79         talloc_free(olck);
80
81         /* after a break to none, we no longer have an oplock attached */
82         if (h->oplock->level == OPLOCK_NONE) {
83                 talloc_free(h->oplock);
84                 h->oplock = NULL;
85         }
86
87         return NT_STATUS_OK;
88 }
89
90 /*
91   receive oplock breaks and forward them to the client
92 */
93 static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
94 {
95         NTSTATUS status;
96         struct pvfs_file *f = opl->file;
97         struct pvfs_file_handle *h = opl->handle;
98         struct pvfs_state *pvfs = h->pvfs;
99         struct timeval cur = timeval_current();
100         struct timeval *last = NULL;
101         struct timeval end;
102
103         switch (level) {
104         case OPLOCK_BREAK_TO_LEVEL_II:
105                 last = &opl->break_to_level_II;
106                 break;
107         case OPLOCK_BREAK_TO_NONE:
108                 last = &opl->break_to_none;
109                 break;
110         }
111
112         if (!last) {
113                 DEBUG(0,("%s: got unexpected level[0x%02X]\n",
114                         __FUNCTION__, level));
115                 return;
116         }
117
118         if (timeval_is_zero(last)) {
119                 /*
120                  * this is the first break we for this level
121                  * remember the time
122                  */
123                 *last = cur;
124
125                 DEBUG(0,("%s: sending oplock break level %d for '%s' %p\n",
126                         __FUNCTION__, level, h->name->original_name, h));
127                 status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
128                 if (!NT_STATUS_IS_OK(status)) {
129                         DEBUG(0,("%s: sending oplock break failed: %s\n",
130                                 __FUNCTION__, nt_errstr(status)));
131                 }
132                 return;
133         }
134
135         end = timeval_add(last, pvfs->oplock_break_timeout, 0);
136
137         if (timeval_compare(&cur, &end) < 0) {
138                 /*
139                  * If it's not expired just ignore the break
140                  * as we already sent the break request to the client
141                  */
142                 DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
143                         __FUNCTION__, level, h->name->original_name, h));
144                 return;
145         }
146
147         /*
148          * If the client did not send a release within the
149          * oplock break timeout time frame we auto release
150          * the oplock
151          */
152         DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
153                 __FUNCTION__, level, h->name->original_name, h));
154         status = pvfs_oplock_release_internal(h, level);
155         if (!NT_STATUS_IS_OK(status)) {
156                 DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
157                         __FUNCTION__, level, nt_errstr(status)));
158         }
159 }
160
161 static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
162                                        void *private_data, uint32_t msg_type,
163                                        struct server_id src, DATA_BLOB *data)
164 {
165         struct pvfs_oplock *opl = talloc_get_type(private_data,
166                                                   struct pvfs_oplock);
167         struct opendb_oplock_break opb;
168
169         ZERO_STRUCT(opb);
170
171         /* we need to check that this one is for us. See
172            messaging_send_ptr() for the other side of this.
173          */
174         if (data->length == sizeof(struct opendb_oplock_break)) {
175                 struct opendb_oplock_break *p;
176                 p = (struct opendb_oplock_break *)data->data;
177                 opb = *p;
178         } else {
179                 DEBUG(0,("%s: ignore oplock break with length[%u]\n",
180                         __location__, data->length));
181                 return;
182         }
183         if (opb.file_handle != opl->handle) {
184                 return;
185         }
186
187         /*
188          * maybe we should use ntvfs_setup_async()
189          */
190         pvfs_oplock_break(opl, opb.level);
191 }
192
193 static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
194 {
195         messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
196         return 0;
197 }
198
199 NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
200 {
201         NTSTATUS status;
202         struct pvfs_oplock *opl;
203         uint32_t level = OPLOCK_NONE;
204
205         f->handle->oplock = NULL;
206
207         switch (oplock_granted) {
208         case EXCLUSIVE_OPLOCK_RETURN:
209                 level = OPLOCK_EXCLUSIVE;
210                 break;
211         case BATCH_OPLOCK_RETURN:
212                 level = OPLOCK_BATCH;
213                 break;
214         case LEVEL_II_OPLOCK_RETURN:
215                 level = OPLOCK_LEVEL_II;
216                 break;
217         }
218
219         if (level == OPLOCK_NONE) {
220                 return NT_STATUS_OK;
221         }
222
223         opl = talloc_zero(f->handle, struct pvfs_oplock);
224         NT_STATUS_HAVE_NO_MEMORY(opl);
225
226         opl->handle     = f->handle;
227         opl->file       = f;
228         opl->level      = level;
229         opl->msg_ctx    = f->pvfs->ntvfs->ctx->msg_ctx;
230
231         status = messaging_register(opl->msg_ctx,
232                                     opl,
233                                     MSG_NTVFS_OPLOCK_BREAK,
234                                     pvfs_oplock_break_dispatch);
235         NT_STATUS_NOT_OK_RETURN(status);
236
237         /* destructor */
238         talloc_set_destructor(opl, pvfs_oplock_destructor);
239
240         f->handle->oplock = opl;
241
242         return NT_STATUS_OK;
243 }
244
245 NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
246                              struct ntvfs_request *req, union smb_lock *lck)
247 {
248         struct pvfs_state *pvfs = ntvfs->private_data;
249         struct pvfs_file *f;
250         uint8_t oplock_break;
251         NTSTATUS status;
252
253         f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
254         if (!f) {
255                 return NT_STATUS_INVALID_HANDLE;
256         }
257
258         oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
259
260         status = pvfs_oplock_release_internal(f->handle, oplock_break);
261         if (!NT_STATUS_IS_OK(status)) {
262                 DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
263                         __FUNCTION__, oplock_break, nt_errstr(status)));
264                 return status;
265         }
266
267         return NT_STATUS_OK;
268 }
269
270 NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
271 {
272         struct pvfs_file_handle *h = f->handle;
273         struct odb_lock *olck;
274         NTSTATUS status;
275
276         if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
277                 return NT_STATUS_OK;
278         }
279
280         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
281         if (olck == NULL) {
282                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
283                 return NT_STATUS_FOOBAR;
284         }
285
286         status = odb_break_oplocks(olck);
287         if (!NT_STATUS_IS_OK(status)) {
288                 DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
289                          h->name->full_name, nt_errstr(status)));
290                 talloc_free(olck);
291                 return status;
292         }
293
294         talloc_free(olck);
295
296         return NT_STATUS_OK;
297 }