/****************************************************************************
Attempt to set an oplock on a file. Succeeds if kernel oplocks are
- disabled (just sets flags) and no byte-range locks in the file. Returns True
- if oplock set.
+ disabled (just sets flags).
****************************************************************************/
NTSTATUS set_file_oplock(files_struct *fsp, int oplock_type)
}
if ((fsp->oplock_type != NO_OPLOCK) &&
- (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
use_kernel &&
!koplocks->ops->set_oplock(koplocks, fsp, oplock_type))
{
struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
if ((fsp->oplock_type != NO_OPLOCK) &&
- (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
koplocks) {
koplocks->ops->release_oplock(koplocks, fsp, NO_OPLOCK);
}
SMB_ASSERT(sconn->oplocks.exclusive_open>=0);
SMB_ASSERT(sconn->oplocks.level_II_open>=0);
- 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->oplock_type = NO_OPLOCK;
fsp->sent_oplock_break = NO_BREAK_SENT;
flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
bool ret;
struct share_mode_lock *lck;
+ DEBUG(10, ("remove_oplock called for %s\n",
+ fsp_str_dbg(fsp)));
+
/* Remove the oplock flag from the sharemode. */
lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
if (lck == NULL) {
"file %s\n", fsp_str_dbg(fsp)));
return False;
}
+
+ if (fsp->oplock_type == LEVEL_II_OPLOCK) {
+
+ /*
+ * If we're the only LEVEL_II holder, we have to remove the
+ * have_read_oplocks from the brlock entry
+ */
+
+ struct share_mode_data *data = lck->data;
+ uint32_t i, num_level2;
+
+ num_level2 = 0;
+ for (i=0; i<data->num_share_modes; i++) {
+ if (data->share_modes[i].op_type == LEVEL_II_OPLOCK) {
+ num_level2 += 1;
+ }
+ if (num_level2 > 1) {
+ /*
+ * No need to count them all...
+ */
+ break;
+ }
+ }
+
+ if (num_level2 == 1) {
+ /*
+ * That's only us. We are dropping that level2 oplock,
+ * so remove the brlock flag.
+ */
+ struct byte_range_lock *brl;
+
+ brl = brl_get_locks(talloc_tos(), fsp);
+ if (brl) {
+ brl_set_have_read_oplocks(brl, false);
+ TALLOC_FREE(brl);
+ }
+ }
+ }
+
ret = remove_share_oplock(lck, fsp);
if (!ret) {
DEBUG(0,("remove_oplock: failed to remove share oplock for "
{
bool ret;
struct share_mode_lock *lck;
+ struct byte_range_lock *brl;
+
+ DEBUG(10, ("downgrade_oplock called for %s\n",
+ fsp_str_dbg(fsp)));
lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
if (lck == NULL) {
}
downgrade_file_oplock(fsp);
+
+ brl = brl_get_locks(talloc_tos(), fsp);
+ if (brl != NULL) {
+ brl_set_have_read_oplocks(brl, true);
+ TALLOC_FREE(brl);
+ }
+
TALLOC_FREE(lck);
return ret;
}
{
files_struct *fsp = NULL;
- if( DEBUGLVL( 3 ) ) {
- dbgtext( "initial_break_processing: called for %s/%u\n",
- file_id_string_tos(&id), (int)file_id);
- dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n",
- sconn->oplocks.exclusive_open,
- sconn->oplocks.level_II_open);
- }
+ DEBUG(3, ("initial_break_processing: called for %s/%u\n"
+ "Current oplocks_open (exclusive = %d, levelII = %d)\n",
+ file_id_string_tos(&id), (int)file_id,
+ sconn->oplocks.exclusive_open,
+ sconn->oplocks.level_II_open));
/*
* We need to search the file open table for the
if(fsp == NULL) {
/* The file could have been closed in the meantime - return success. */
- if( DEBUGLVL( 3 ) ) {
- dbgtext( "initial_break_processing: cannot find open file with " );
- dbgtext( "file_id %s gen_id = %lu, ", file_id_string_tos(&id), file_id);
- dbgtext( "allowing break to succeed.\n" );
- }
+ DEBUG(3, ("initial_break_processing: cannot find open file "
+ "with file_id %s gen_id = %lu, allowing break to "
+ "succeed.\n", file_id_string_tos(&id), file_id));
return NULL;
}
*/
if(fsp->oplock_type == NO_OPLOCK) {
- if( DEBUGLVL( 3 ) ) {
- dbgtext( "initial_break_processing: file %s ",
- fsp_str_dbg(fsp));
- dbgtext( "(file_id = %s gen_id = %lu) has no oplock.\n",
- file_id_string_tos(&id), fsp->fh->gen_id );
- dbgtext( "Allowing break to succeed regardless.\n" );
- }
+ DEBUG(3, ("initial_break_processing: file %s (file_id = %s "
+ "gen_id = %lu) has no oplock. Allowing break to "
+ "succeed regardless.\n", fsp_str_dbg(fsp),
+ file_id_string_tos(&id), fsp->fh->gen_id));
return NULL;
}
}
}
-static void break_level2_to_none_async(files_struct *fsp)
-{
- struct smbd_server_connection *sconn = fsp->conn->sconn;
-
- if (fsp->oplock_type == NO_OPLOCK) {
- /* We already got a "break to none" message and we've handled
- * it. just ignore. */
- DEBUG(3, ("process_oplock_async_level2_break_message: already "
- "broken to none, ignoring.\n"));
- return;
- }
-
- if (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
- /* Don't tell the client, just downgrade. */
- DEBUG(3, ("process_oplock_async_level2_break_message: "
- "downgrading fake level 2 oplock.\n"));
- remove_oplock(fsp);
- return;
- }
-
- /* 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 %s, file %s\n", fsp_fnum_dbg(fsp),
- fsp_str_dbg(fsp)));
-
- /* Now send a break to none message to our client. */
- if (sconn->using_smb2) {
- send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
- } else {
- send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
- }
-
- /* Async level2 request, don't send a reply, just remove the oplock. */
- remove_oplock(fsp);
-}
-
/*******************************************************************
This handles the case of a write triggering a break to none
message on a level2 oplock.
- When we get this message we may be in any of three states :
- NO_OPLOCK, LEVEL_II, FAKE_LEVEL2. We only send a message to
+ When we get this message we may be in any of two states :
+ NO_OPLOCK, LEVEL_II. We only send a message to
the client for LEVEL2.
*******************************************************************/
struct smbd_server_connection *sconn =
talloc_get_type_abort(private_data,
struct smbd_server_connection);
+ struct server_id self = messaging_server_id(sconn->msg_ctx);
if (data->data == NULL) {
DEBUG(0, ("Got NULL buffer\n"));
return;
}
- break_level2_to_none_async(fsp);
+
+ if (fsp->oplock_type == NO_OPLOCK) {
+ /* We already got a "break to none" message and we've handled
+ * it. just ignore. */
+ DEBUG(3, ("process_oplock_async_level2_break_message: already "
+ "broken to none, ignoring.\n"));
+ return;
+ }
+
+ /* 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 %s, file %s\n", fsp_fnum_dbg(fsp),
+ fsp_str_dbg(fsp)));
+
+ /* Need to wait before sending a break
+ message if we sent ourselves this message. */
+ if (serverid_equal(&self, &src)) {
+ wait_before_sending_break();
+ }
+
+ /* Now send a break to none message to our client. */
+ if (sconn->using_smb2) {
+ send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
+ } else {
+ send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
+ }
+
+ /* Async level2 request, don't send a reply, just remove the oplock. */
+ remove_oplock(fsp);
}
/*******************************************************************
struct smbd_server_connection);
struct server_id self = messaging_server_id(sconn->msg_ctx);
struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+ uint16_t break_to;
if (data->data == NULL) {
DEBUG(0, ("Got NULL buffer\n"));
/* De-linearize incoming message. */
message_to_share_mode_entry(&msg, (char *)data->data);
+ break_to = msg.op_type;
- DEBUG(10, ("Got oplock break message from pid %s: %s/%llu\n",
- server_id_str(talloc_tos(), &src),
+ DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n",
+ (unsigned)break_to, server_id_str(talloc_tos(), &src),
file_id_string_tos(&msg.id),
(unsigned long long)msg.share_file_id));
return;
}
- if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) &&
- !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ if (break_to == fsp->oplock_type) {
DEBUG(3, ("Already downgraded oplock on %s: %s\n",
file_id_string_tos(&fsp->file_id),
fsp_str_dbg(fsp)));
use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ (break_to != NO_OPLOCK) &&
!(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
lp_level2_oplocks(SNUM(fsp->conn))) {
break_to_level2 = True;
OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
}
+ if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+ /*
+ * This is an async break without a reply and thus no timeout
+ */
+ remove_oplock(fsp);
+ return;
+ }
fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
-
add_oplock_timeout_handler(fsp);
}
struct smbd_server_connection *sconn = fsp->conn->sconn;
struct tevent_immediate *im;
struct break_to_none_state *state;
+ struct byte_range_lock *brl;
/*
* If this file is level II oplocked then we need
* the shared memory area whilst doing this.
*/
- if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ /*
+ * There can't be any level2 oplocks, we're alone.
+ */
return;
+ }
+
+ brl = brl_get_locks_readonly(fsp);
+ if ((brl != NULL) && !brl_have_read_oplocks(brl)) {
+ DEBUG(10, ("No read oplocks around\n"));
+ return;
+ }
/*
* When we get here we might have a brlock entry locked. Also
* locking the share mode entry would violate the locking
* order. Breaking level2 oplocks to none is asynchronous
- * anyway, so we postpone this into an immediate timed event.
+ * anyway, so we postpone this into an immediate event.
*/
state = talloc(sconn, struct break_to_none_state);
{
struct break_to_none_state *state = talloc_get_type_abort(
private_data, struct break_to_none_state);
- struct server_id self = messaging_server_id(state->sconn->msg_ctx);
int i;
struct share_mode_lock *lck;
* As there could have been multiple writes waiting at the
* lock_share_entry gate we may not be the first to
* enter. Hence the state of the op_types in the share mode
- * entries may be partly NO_OPLOCK and partly LEVEL_II or FAKE_LEVEL_II
+ * entries may be partly NO_OPLOCK and partly LEVEL_II
* oplock. It will do no harm to re-send break messages to
* those smbd's that are still waiting their turn to remove
* their LEVEL_II state, and also no harm to ignore existing
}
share_mode_entry_to_message(msg, share_entry);
+ /* Overload entry->op_type */
+ SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
- /*
- * 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 (serverid_equal(&self, &share_entry->pid)) {
- struct files_struct *cur_fsp =
- initial_break_processing(state->sconn,
- share_entry->id,
- share_entry->share_file_id);
- if (cur_fsp != NULL) {
- wait_before_sending_break();
- break_level2_to_none_async(cur_fsp);
- } else {
- DEBUG(3, ("release_level_2_oplocks_on_change: "
- "Did not find fsp, ignoring\n"));
- }
- } else {
- messaging_send_buf(state->sconn->msg_ctx,
- share_entry->pid,
- MSG_SMB_ASYNC_LEVEL2_BREAK,
- (uint8 *)msg, sizeof(msg));
- }
+ messaging_send_buf(state->sconn->msg_ctx, share_entry->pid,
+ MSG_SMB_BREAK_REQUEST,
+ (uint8 *)msg, sizeof(msg));
}
/* We let the message receivers handle removing the oplock state