#define DBGC_CLASS DBGC_LOCKING
#include "includes.h"
-
-/* Current number of oplocks we have outstanding. */
-static int32 exclusive_oplocks_open = 0;
-static int32 level_II_oplocks_open = 0;
-bool global_client_failed_oplock_break = False;
-
-extern uint32 global_client_caps;
-
-static struct kernel_oplocks *koplocks;
+#include "smbd/globals.h"
/****************************************************************************
Get the number of current exclusive oplocks.
return exclusive_oplocks_open;
}
-/****************************************************************************
- Return True if an oplock message is pending.
-****************************************************************************/
-
-bool oplock_message_waiting(fd_set *fds)
-{
- if (koplocks && koplocks->msg_waiting(fds)) {
- return True;
- }
-
- return False;
-}
-
-/****************************************************************************
- Find out if there are any kernel oplock messages waiting and process them
- if so. pfds is the fd_set from the main select loop (which contains any
- kernel oplock fd if that's what the system uses (IRIX). If may be NULL if
- we're calling this in a shutting down state.
-****************************************************************************/
-
-void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds)
+/*
+ * helper function used by the kernel oplock backends to post the break message
+ */
+void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp)
{
- /*
- * We need to check for kernel oplocks before going into the select
- * here, as the EINTR generated by the linux kernel oplock may have
- * already been eaten. JRA.
- */
-
- if (!koplocks) {
- return;
- }
-
- while (koplocks->msg_waiting(pfds)) {
- files_struct *fsp;
- char msg[MSG_SMB_KERNEL_BREAK_SIZE];
-
- fsp = koplocks->receive_message(pfds);
-
- if (fsp == NULL) {
- DEBUG(3, ("Kernel oplock message announced, but none "
- "received\n"));
- return;
- }
+ uint8_t msg[MSG_SMB_KERNEL_BREAK_SIZE];
- /* Put the kernel break info into the message. */
- push_file_id_16(msg, &fsp->file_id);
- SIVAL(msg,16,fsp->fh->gen_id);
+ /* Put the kernel break info into the message. */
+ push_file_id_16((char *)msg, &fsp->file_id);
+ SIVAL(msg,16,fsp->fh->gen_id);
- /* Don't need to be root here as we're only ever
- sending to ourselves. */
+ /* Don't need to be root here as we're only ever
+ sending to ourselves. */
- messaging_send_buf(msg_ctx, procid_self(),
- MSG_SMB_KERNEL_BREAK,
- (uint8 *)&msg, MSG_SMB_KERNEL_BREAK_SIZE);
- }
+ messaging_send_buf(msg_ctx, procid_self(),
+ MSG_SMB_KERNEL_BREAK,
+ msg, MSG_SMB_KERNEL_BREAK_SIZE);
}
/****************************************************************************
bool set_file_oplock(files_struct *fsp, int oplock_type)
{
- if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) {
+ if ((fsp->oplock_type != NO_OPLOCK) &&
+ (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
+ koplocks &&
+ !koplocks->ops->set_oplock(koplocks, fsp, oplock_type)) {
return False;
}
fsp->sent_oplock_break = NO_BREAK_SENT;
if (oplock_type == LEVEL_II_OPLOCK) {
level_II_oplocks_open++;
- } else {
+ } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
exclusive_oplocks_open++;
}
if ((fsp->oplock_type != NO_OPLOCK) &&
(fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
koplocks) {
- koplocks->release_oplock(fsp);
+ koplocks->ops->release_oplock(koplocks, fsp);
}
if (fsp->oplock_type == LEVEL_II_OPLOCK) {
SMB_ASSERT(exclusive_oplocks_open>=0);
SMB_ASSERT(level_II_oplocks_open>=0);
-
- fsp->oplock_type = NO_OPLOCK;
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ /* This doesn't matter for close. */
+ fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+ } else {
+ fsp->oplock_type = NO_OPLOCK;
+ }
fsp->sent_oplock_break = NO_BREAK_SENT;
-
+
flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
TALLOC_FREE(fsp->oplock_timeout);
static void downgrade_file_oplock(files_struct *fsp)
{
if (koplocks) {
- koplocks->release_oplock(fsp);
+ koplocks->ops->release_oplock(koplocks, fsp);
}
fsp->oplock_type = LEVEL_II_OPLOCK;
exclusive_oplocks_open--;
struct share_mode_lock *lck;
/* Remove the oplock flag from the sharemode. */
- lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL);
+ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+ NULL);
if (lck == NULL) {
DEBUG(0,("remove_oplock: failed to lock share entry for "
"file %s\n", fsp->fsp_name ));
bool ret;
struct share_mode_lock *lck;
- lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL);
+ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+ NULL);
if (lck == NULL) {
DEBUG(0,("downgrade_oplock: failed to lock share entry for "
"file %s\n", fsp->fsp_name ));
return ret;
}
-/****************************************************************************
- Return the fd (if any) used for receiving oplock notifications.
-****************************************************************************/
-
-int oplock_notify_fd(void)
-{
- if (koplocks) {
- return koplocks->notification_fd;
- }
-
- return -1;
-}
-
/****************************************************************************
Set up an oplock break message.
****************************************************************************/
static void oplock_timeout_handler(struct event_context *ctx,
struct timed_event *te,
- const struct timeval *now,
+ struct timeval now,
void *private_data)
{
files_struct *fsp = (files_struct *)private_data;
fsp->oplock_timeout =
event_add_timed(smbd_event_context(), NULL,
timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
- "oplock_timeout_handler",
oplock_timeout_handler, fsp);
if (fsp->oplock_timeout == NULL) {
/* Ensure we're really at level2 state. */
SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK);
+ DEBUG(10,("process_oplock_async_level2_break_message: sending break to "
+ "none message for fid %d, file %s\n",
+ fsp->fnum,
+ fsp->fsp_name));
+
/* Now send a break to none message to our client. */
break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE);
if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
return;
- lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL);
+ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+ NULL);
if (lck == NULL) {
DEBUG(0,("release_level_2_oplocks_on_change: failed to lock "
"share mode entry for file %s.\n", fsp->fsp_name ));
share_mode_entry_to_message(msg, share_entry);
- messaging_send_buf(smbd_messaging_context(), share_entry->pid,
- MSG_SMB_ASYNC_LEVEL2_BREAK,
- (uint8 *)msg,
- MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+ /*
+ * Deal with a race condition when breaking level2
+ * oplocks. Don't send all the messages and release
+ * the lock, this allows someone else to come in and
+ * get a level2 lock before any of the messages are
+ * processed, and thus miss getting a break message.
+ * Ensure at least one entry (the one we're breaking)
+ * is processed immediately under the lock and becomes
+ * set as NO_OPLOCK to stop any waiter getting a level2.
+ * Bugid #5980.
+ */
+
+ if (procid_is_me(&share_entry->pid)) {
+ DATA_BLOB blob = data_blob_const(msg,
+ MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+ process_oplock_async_level2_break_message(smbd_messaging_context(),
+ NULL,
+ MSG_SMB_ASYNC_LEVEL2_BREAK,
+ share_entry->pid,
+ &blob);
+ } else {
+ messaging_send_buf(smbd_messaging_context(),
+ share_entry->pid,
+ MSG_SMB_ASYNC_LEVEL2_BREAK,
+ (uint8 *)msg,
+ MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+ }
}
/* We let the message receivers handle removing the oplock state
Linearize a share mode entry struct to an internal oplock break message.
****************************************************************************/
-void share_mode_entry_to_message(char *msg, struct share_mode_entry *e)
+void share_mode_entry_to_message(char *msg, const struct share_mode_entry *e)
{
SIVAL(msg,0,(uint32)e->pid.pid);
SSVAL(msg,4,e->op_mid);
if (lp_kernel_oplocks()) {
#if HAVE_KERNEL_OPLOCKS_IRIX
- koplocks = irix_init_kernel_oplocks();
+ koplocks = irix_init_kernel_oplocks(talloc_autofree_context());
#elif HAVE_KERNEL_OPLOCKS_LINUX
- koplocks = linux_init_kernel_oplocks();
+ koplocks = linux_init_kernel_oplocks(talloc_autofree_context());
#endif
}