From b2d01bd2dbfed8b35cc324fad42eac562fcad3b4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 12 Jun 2000 15:53:31 +0000 Subject: totally rewrote the async signal, notification and oplock notification handling in Samba. This was needed due to several limitations and races in the previous code - as a side effect the new code is much cleaner :) in summary: - changed sys_select() to avoid a signal/select race condition. It is a rare race but once we have signals doing notification and oplocks it is important. - changed our main processing loop to take advantage of the new sys_select semantics - split the notify code into implementaion dependent and general parts. Added the following structure that defines an implementation: struct cnotify_fns { void * (*register_notify)(connection_struct *conn, char *path, uint32 flags); BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t); void (*remove_notify)(void *data); }; then I wrote two implementations, one using hash/poll (like our old code) and the other using the new Linux kernel change notify. It should be easy to add other change notify implementations by creating a sructure of the above type. - fixed a bug in change notify where we were returning the wrong error code. - rewrote the core change notify code to be much simpler - moved to real-time signals for leases and change notify Amazingly, it all seems to work. I was very surprised! (This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267) --- source3/smbd/process.c | 253 +++++++++++++++++++++++-------------------------- 1 file changed, 120 insertions(+), 133 deletions(-) (limited to 'source3/smbd/process.c') diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 30d03747d8..b84e55343e 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -107,7 +107,30 @@ static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len) BOOL push_oplock_pending_smb_message(char *buf, int msg_len) { - return push_message(&smb_oplock_queue, buf, msg_len); + return push_message(&smb_oplock_queue, buf, msg_len); +} + +/**************************************************************************** +do all async processing in here. This includes UDB oplock messages, kernel +oplock messages, change notify events etc. +****************************************************************************/ +static void async_processing(fd_set *fds, char *buffer, int buffer_len) +{ + /* check for oplock messages (both UDP and kernel) */ + if (receive_local_message(fds, buffer, buffer_len, 0)) { + process_local_message(buffer, buffer_len); + } + + /* check for async change notify events */ + process_pending_change_notify_queue(0); + + /* check for sighup processing */ + if (reload_after_sighup) { + unbecome_user(); + DEBUG(1,("Reloading services after SIGHUP\n")); + reload_services(False); + reload_after_sighup = False; + } } /**************************************************************************** @@ -115,7 +138,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len) If a local udp message has been pushed onto the queue (this can only happen during oplock break - processing) return this first. + processing) call async_processing() If a pending smb message has been pushed onto the queue (this can only happen during oplock break @@ -131,8 +154,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len) The timeout is in milli seconds ****************************************************************************/ -static BOOL receive_message_or_smb(char *buffer, int buffer_len, - int timeout, BOOL *got_smb) +static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) { fd_set fds; int selrtn; @@ -141,30 +163,28 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, smb_read_error = 0; - *got_smb = False; - /* * Check to see if we already have a message on the smb queue. * If so - copy and return it. */ - - if(ubi_slCount(&smb_oplock_queue) != 0) { + if(ubi_slCount(&smb_oplock_queue) != 0) { pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue); memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len)); /* Free the message we just copied. */ free((char *)msg->msg_buf); free((char *)msg); - *got_smb = True; DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); return True; } + /* * Setup the select read fd set. */ + again: FD_ZERO(&fds); FD_SET(smbd_server_fd(),&fds); maxfd = setup_oplock_select_set(&fds); @@ -175,16 +195,16 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,timeout>0?&to:NULL); /* if we get EINTR then maybe we have received an oplock - signal - treat this as select returning 1. This is ugly, but - is the best we can do until the oplock code knows more about - signals */ + signal - treat this as select returning 1. This is ugly, but + is the best we can do until the oplock code knows more about + signals */ if (selrtn == -1 && errno == EINTR) { - FD_ZERO(&fds); - selrtn = 1; + async_processing(&fds, buffer, buffer_len); + goto again; } /* Check if error */ - if(selrtn == -1 && errno != EINTR) { + if (selrtn == -1) { /* something is wrong. Maybe the socket is dead? */ smb_read_error = READ_ERROR; return False; @@ -195,13 +215,13 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, smb_read_error = READ_TIMEOUT; return False; } - - if (FD_ISSET(smbd_server_fd(),&fds)) { - *got_smb = True; - return receive_smb(smbd_server_fd(), buffer, 0); - } else { - return receive_local_message(&fds, buffer, buffer_len, 0); + + if (!FD_ISSET(smbd_server_fd(),&fds) || selrtn > 1) { + async_processing(&fds, buffer, buffer_len); + if (!FD_ISSET(smbd_server_fd(),&fds)) goto again; } + + return receive_smb(smbd_server_fd(), buffer, 0); } /**************************************************************************** @@ -210,30 +230,16 @@ Get the next SMB packet, doing the local message processing automatically. BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) { - BOOL got_smb = False; - BOOL ret; - - do - { - ret = receive_message_or_smb(inbuf,bufsize,timeout,&got_smb); + BOOL got_keepalive; + BOOL ret; - if(ret && !got_smb) - { - /* Deal with oplock break requests from other smbd's. */ - process_local_message(inbuf, bufsize); - continue; - } - - if(ret && (CVAL(inbuf,0) == 0x85)) - { - /* Keepalive packet. */ - got_smb = False; - } - - } - while(ret && !got_smb); + do { + ret = receive_message_or_smb(inbuf,bufsize,timeout); + + got_keepalive = (ret && (CVAL(inbuf,0) == 0x85)); + } while (ret && got_keepalive); - return ret; + return ret; } /**************************************************************************** @@ -270,13 +276,12 @@ void respond_to_all_remaining_local_messages(void) * Keep doing receive_local_message with a 1 ms timeout until * we have no more messages. */ - while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) { - /* Deal with oplock break requests from other smbd's. */ - process_local_message(buffer, sizeof(buffer)); + /* Deal with oplock break requests from other smbd's. */ + process_local_message(buffer, sizeof(buffer)); - FD_ZERO(&fds); - (void)setup_oplock_select_set(&fds); + FD_ZERO(&fds); + (void)setup_oplock_select_set(&fds); } return; @@ -1008,98 +1013,80 @@ machine %s in domain %s.\n", global_myname, global_myworkgroup )); void smbd_process(void) { - extern int smb_echo_count; - time_t last_timeout_processing_time = time(NULL); - unsigned int num_smbs = 0; + extern int smb_echo_count; + time_t last_timeout_processing_time = time(NULL); + unsigned int num_smbs = 0; - InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - if ((InBuffer == NULL) || (OutBuffer == NULL)) - return; + InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return; - InBuffer += SMB_ALIGNMENT; - OutBuffer += SMB_ALIGNMENT; + InBuffer += SMB_ALIGNMENT; + OutBuffer += SMB_ALIGNMENT; - max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); + max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); - /* re-initialise the timezone */ - TimeInit(); + /* re-initialise the timezone */ + TimeInit(); - while (True) - { - int deadtime = lp_deadtime()*60; - BOOL got_smb = False; - int select_timeout = setup_select_timeout(); - - if (deadtime <= 0) - deadtime = DEFAULT_SMBD_TIMEOUT; - - errno = 0; - - /* free up temporary memory */ - lp_talloc_free(); - - /* - * If reload_after_sighup == True then we got a SIGHUP - * and are being asked to reload. Fix from - */ - if (reload_after_sighup) { - /* become root */ - unbecome_user(); - DEBUG(1,("Reloading services after SIGHUP\n")); - reload_services(False); - reload_after_sighup = False; - } - - while(!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout,&got_smb)) - { - if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) - return; - num_smbs = 0; /* Reset smb counter. */ - } - - if(got_smb) { - /* - * Ensure we do timeout processing if the SMB we just got was - * only an echo request. This allows us to set the select - * timeout in 'receive_message_or_smb()' to any value we like - * without worrying that the client will send echo requests - * faster than the select timeout, thus starving out the - * essential processing (change notify, blocking locks) that - * the timeout code does. JRA. - */ - int num_echos = smb_echo_count; - - process_smb(InBuffer, OutBuffer); - - if(smb_echo_count != num_echos) { - if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) - return; - num_smbs = 0; /* Reset smb counter. */ - } - - num_smbs++; + while (True) { + int deadtime = lp_deadtime()*60; + int select_timeout = setup_select_timeout(); + int num_echos; - /* - * If we are getting smb requests in a constant stream - * with no echos, make sure we attempt timeout processing - * every select_timeout milliseconds - but only check for this - * every 200 smb requests. - */ + if (deadtime <= 0) + deadtime = DEFAULT_SMBD_TIMEOUT; - if((num_smbs % 200) == 0) { - time_t new_check_time = time(NULL); - if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) { - if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) - return; - num_smbs = 0; /* Reset smb counter. */ - last_timeout_processing_time = new_check_time; /* Reset time. */ - } - } - } - else - process_local_message(InBuffer, BUFFER_SIZE); - } + errno = 0; + + /* free up temporary memory */ + lp_talloc_free(); + + while (!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout)) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + } + + /* + * Ensure we do timeout processing if the SMB we just got was + * only an echo request. This allows us to set the select + * timeout in 'receive_message_or_smb()' to any value we like + * without worrying that the client will send echo requests + * faster than the select timeout, thus starving out the + * essential processing (change notify, blocking locks) that + * the timeout code does. JRA. + */ + num_echos = smb_echo_count; + + process_smb(InBuffer, OutBuffer); + + if (smb_echo_count != num_echos) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + } + + num_smbs++; + + /* + * If we are getting smb requests in a constant stream + * with no echos, make sure we attempt timeout processing + * every select_timeout milliseconds - but only check for this + * every 200 smb requests. + */ + + if ((num_smbs % 200) == 0) { + time_t new_check_time = time(NULL); + if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + last_timeout_processing_time = new_check_time; /* Reset time. */ + } + } + } } #undef OLD_NTDOMAIN -- cgit