diff options
author | Jeremy Allison <jra@samba.org> | 2006-07-18 01:05:51 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:38:12 -0500 |
commit | b737f26764cce935d9482335ece11c71a96720f4 (patch) | |
tree | e2d21c8b53815c90092437b2c4dc14bf5b34e560 /source3/smbd/blocking.c | |
parent | 94ad8543bd27c6cbdcdaf4acf84d91438dbef28b (diff) | |
download | samba-b737f26764cce935d9482335ece11c71a96720f4.tar.gz samba-b737f26764cce935d9482335ece11c71a96720f4.tar.xz samba-b737f26764cce935d9482335ece11c71a96720f4.zip |
r17105: Fix the race Volker found - we had a non-locked
region between detecting a pending lock was needed
and when we added the blocking lock record. Make
sure that we hold the lock over all this period.
Removed the old code for doing blocking locks on
SMB requests that never block (the old SMBlock
and friends).
Discovered something interesting about the strange
NT_STATUS_FILE_LOCK_CONFLICT return. If we asked
for a lock with zero timeout, and we got an error
of NT_STATUS_FILE_LOCK_CONFLICT, treat it as though
it was a blocking lock with a timeout of 150 - 300ms.
This only happens when timeout is sent as zero and
can be seen quite clearly in ethereal. This is the
real replacement for old do_lock_spin() code.
Re-worked the blocking lock select timeout to correctly
use milliseconds instead of the old second level
resolution (far too coarse for this work).
Jeremy.
(This used to be commit b81d6d1ae95a3d3e449dde629884b565eac289d9)
Diffstat (limited to 'source3/smbd/blocking.c')
-rw-r--r-- | source3/smbd/blocking.c | 261 |
1 files changed, 64 insertions, 197 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 941e87d3ad7..a0b93f50320 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -32,7 +32,7 @@ typedef struct _blocking_lock_record { struct _blocking_lock_record *prev; int com_type; files_struct *fsp; - time_t expire_time; + struct timeval expire_time; int lock_num; SMB_BIG_UINT offset; SMB_BIG_UINT count; @@ -75,7 +75,8 @@ static void received_unlock_msg(int msg_type, struct process_id src, Function to push a blocking lock request onto the lock queue. ****************************************************************************/ -BOOL push_blocking_lock_request( char *inbuf, int length, +BOOL push_blocking_lock_request( struct byte_range_lock *br_lck, + char *inbuf, int length, files_struct *fsp, int lock_timeout, int lock_num, @@ -86,7 +87,6 @@ BOOL push_blocking_lock_request( char *inbuf, int length, { static BOOL set_lock_msg; blocking_lock_record *blr, *tmp; - struct byte_range_lock *br_lck = NULL; NTSTATUS status; if(in_chained_smb() ) { @@ -115,7 +115,13 @@ BOOL push_blocking_lock_request( char *inbuf, int length, blr->com_type = CVAL(inbuf,smb_com); blr->fsp = fsp; - blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout; + if (lock_timeout == -1) { + blr->expire_time.tv_sec = 0; + blr->expire_time.tv_usec = 0; /* Never expire. */ + } else { + blr->expire_time = timeval_current_ofs(lock_timeout/1000, + (lock_timeout % 1000) * 1000); + } blr->lock_num = lock_num; blr->lock_pid = lock_pid; blr->lock_flav = lock_flav; @@ -125,13 +131,6 @@ BOOL push_blocking_lock_request( char *inbuf, int length, memcpy(blr->inbuf, inbuf, length); blr->length = length; - br_lck = brl_get_locks(NULL, blr->fsp); - if (!br_lck) { - DLIST_REMOVE(blocking_lock_queue, blr); - free_blocking_lock_record(blr); - return False; - } - /* Add a pending lock record for this. */ status = brl_lock(br_lck, lock_pid, @@ -140,8 +139,7 @@ BOOL push_blocking_lock_request( char *inbuf, int length, count, PENDING_LOCK, blr->lock_flav, - lock_timeout); - TALLOC_FREE(br_lck); + lock_timeout ? True : False); /* blocking_lock. */ if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n")); @@ -158,8 +156,10 @@ BOOL push_blocking_lock_request( char *inbuf, int length, set_lock_msg = True; } - DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \ -for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout, + DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with " + "expiry time (%u sec. %u usec) (+%d msec) for fnum = %d, name = %s\n", + length, (unsigned int)blr->expire_time.tv_sec, + (unsigned int)blr->expire_time.tv_usec, lock_timeout, blr->fsp->fnum, blr->fsp->fsp_name )); /* Push the MID of this packet on the signing queue. */ @@ -305,13 +305,6 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status) { switch(blr->com_type) { -#if 0 - /* We no longer push blocking lock requests for anything but lockingX and trans2. */ - case SMBlock: - case SMBlockread: - generic_blocking_lock_error(blr, status); - break; -#endif case SMBlockingX: reply_lockingX_error(blr, status); break; @@ -337,146 +330,6 @@ static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status } } -#if 0 -/* We no longer push blocking lock requests for anything but lockingX and trans2. */ - -/**************************************************************************** - Attempt to finish off getting all pending blocking locks for a lockread call. - Returns True if we want to be removed from the list. -*****************************************************************************/ - -static BOOL process_lockread(blocking_lock_record *blr) -{ - char *outbuf = get_OutBuffer(); - char *inbuf = blr->inbuf; - ssize_t nread = -1; - char *data, *p; - int outsize = 0; - SMB_BIG_UINT startpos; - size_t numtoread; - NTSTATUS status; - files_struct *fsp = blr->fsp; - - numtoread = SVAL(inbuf,smb_vwv1); - startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2); - - numtoread = MIN(BUFFER_SIZE-outsize,numtoread); - data = smb_buf(outbuf) + 3; - - status = do_lock(fsp, - (uint32)SVAL(inbuf,smb_pid), - (SMB_BIG_UINT)numtoread, - startpos, - READ_LOCK, - WINDOWS_LOCK, - (int32)-1); - - if (NT_STATUS_V(status)) { - if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { - /* - * We have other than a "can't get lock" - * error. Send an error. - * Return True so we get dequeued. - */ - generic_blocking_lock_error(blr, status); - return True; - } - - /* - * Still waiting for lock.... - */ - - DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n", - fsp->fsp_name)); - return False; - } - - nread = read_file(fsp,data,startpos,numtoread); - - if (nread < 0) { - generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED); - return True; - } - - construct_reply_common(inbuf, outbuf); - outsize = set_message(outbuf,5,0,True); - - outsize += nread; - SSVAL(outbuf,smb_vwv0,nread); - SSVAL(outbuf,smb_vwv5,nread+3); - p = smb_buf(outbuf); - *p++ = 1; - SSVAL(p,0,nread); p += 2; - set_message_end(outbuf, p+nread); - - DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%lu nread=%ld\n", - fsp->fsp_name, fsp->fnum, (unsigned long)numtoread, (long)nread ) ); - - send_blocking_reply(outbuf,outsize); - return True; -} - -/**************************************************************************** - Attempt to finish off getting all pending blocking locks for a lock call. - Returns True if we want to be removed from the list. -*****************************************************************************/ - -static BOOL process_lock(blocking_lock_record *blr) -{ - char *outbuf = get_OutBuffer(); - char *inbuf = blr->inbuf; - int outsize; - SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; - NTSTATUS status; - files_struct *fsp = blr->fsp; - - count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1); - offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); - - errno = 0; - status = do_lock(fsp, - (uint32)SVAL(inbuf,smb_pid), - count, - offset, - WRITE_LOCK, - WINDOWS_LOCK, - (int32)-1); - - if (NT_STATUS_IS_ERR(status)) { - if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { - /* - * We have other than a "can't get lock" - * error. Send an error. - * Return True so we get dequeued. - */ - - blocking_lock_reply_error(blr, status); - return True; - } - /* - * Still can't get the lock - keep waiting. - */ - DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n", - fsp->fsp_name)); - return False; - } - - /* - * Success - we got the lock. - */ - - DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n", - fsp->fsp_name, fsp->fnum, (double)offset, (double)count)); - - construct_reply_common(inbuf, outbuf); - outsize = set_message(outbuf,0,0,True); - send_blocking_reply(outbuf,outsize); - return True; -} -#endif - /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockingX call. Returns True if we want to be removed from the list. @@ -501,8 +354,9 @@ static BOOL process_lockingX(blocking_lock_record *blr) * Data now points at the beginning of the list * of smb_lkrng structs. */ - + for(; blr->lock_num < num_locks; blr->lock_num++) { + struct byte_range_lock *br_lck = NULL; BOOL err; lock_pid = get_lock_pid( data, blr->lock_num, large_file_format); @@ -514,14 +368,17 @@ static BOOL process_lockingX(blocking_lock_record *blr) * request would never have been queued. JRA. */ errno = 0; - status = do_lock(fsp, + br_lck = do_lock(fsp, lock_pid, count, offset, ((locktype & LOCKING_ANDX_SHARED_LOCK) ? READ_LOCK : WRITE_LOCK), WINDOWS_LOCK, - (int32)-1); + True, + &status); + + TALLOC_FREE(br_lck); if (NT_STATUS_IS_ERR(status)) { break; @@ -573,14 +430,15 @@ static BOOL process_trans2(blocking_lock_record *blr) char *outbuf; char params[2]; NTSTATUS status; - - status = do_lock(blr->fsp, - blr->lock_pid, - blr->count, - blr->offset, - blr->lock_type, - blr->lock_flav, - (int32)-1); + struct byte_range_lock *br_lck = do_lock(blr->fsp, + blr->lock_pid, + blr->count, + blr->offset, + blr->lock_type, + blr->lock_flav, + True, + &status); + TALLOC_FREE(br_lck); if (!NT_STATUS_IS_OK(status)) { if (ERROR_WAS_LOCK_DENIED(status)) { @@ -613,13 +471,6 @@ static BOOL process_trans2(blocking_lock_record *blr) static BOOL blocking_lock_record_process(blocking_lock_record *blr) { switch(blr->com_type) { -#if 0 - /* We no longer push blocking lock requests for anything but lockingX and trans2. */ - case SMBlock: - return process_lock(blr); - case SMBlockread: - return process_lockread(blr); -#endif case SMBlockingX: return process_lockingX(blr); case SMBtrans2: @@ -714,44 +565,60 @@ static void received_unlock_msg(int msg_type, struct process_id src, void *buf, size_t len) { DEBUG(10,("received_unlock_msg\n")); - process_blocking_lock_queue(time(NULL)); + process_blocking_lock_queue(); } /**************************************************************************** - Return the number of seconds to the next blocking locks timeout, or default_timeout + Return the number of milliseconds to the next blocking locks timeout, or default_timeout *****************************************************************************/ -unsigned blocking_locks_timeout(unsigned default_timeout) +unsigned int blocking_locks_timeout_ms(unsigned int default_timeout_ms) { - unsigned timeout = default_timeout; - time_t t; + unsigned int timeout_ms = default_timeout_ms; + struct timeval tv_curr; + SMB_BIG_INT min_tv_dif_us = 0x7FFFFFFF; /* A large +ve number. */ blocking_lock_record *blr = blocking_lock_queue; - /* note that we avoid the time() syscall if there are no blocking locks */ - if (!blr) - return timeout; + /* note that we avoid the GetTimeOfDay() syscall if there are no blocking locks */ + if (!blr) { + return timeout_ms; + } - t = time(NULL); + tv_curr = timeval_current(); for (; blr; blr = blr->next) { - if ((blr->expire_time != (time_t)-1) && - (timeout > (blr->expire_time - t))) { - timeout = blr->expire_time - t; + SMB_BIG_INT tv_dif_us; + + if (timeval_is_zero(&blr->expire_time)) { + continue; /* Never timeout. */ } + + tv_dif_us = usec_time_diff(&blr->expire_time, &tv_curr); + min_tv_dif_us = MIN(min_tv_dif_us, tv_dif_us); + } + + if (min_tv_dif_us < 0) { + min_tv_dif_us = 0; + } + + timeout_ms = (unsigned int)(min_tv_dif_us / (SMB_BIG_INT)1000); + + if (timeout_ms < 1) { + timeout_ms = 1; } - if (timeout < 1) - timeout = 1; + DEBUG(10,("blocking_locks_timeout_ms: returning %u\n", timeout_ms)); - return timeout; + return timeout_ms; } /**************************************************************************** Process the blocking lock queue. Note that this is only called as root. *****************************************************************************/ -void process_blocking_lock_queue(time_t t) +void process_blocking_lock_queue(void) { + struct timeval tv_curr = timeval_current(); blocking_lock_record *blr, *next = NULL; /* @@ -780,7 +647,7 @@ void process_blocking_lock_queue(time_t t) DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n", fsp->fnum, fsp->fsp_name )); - if((blr->expire_time != -1) && (blr->expire_time <= t)) { + if (!timeval_is_zero(&blr->expire_time) && timeval_compare(&blr->expire_time, &tv_curr) <= 0) { struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); /* |