Commit 5f66d2b5 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  CIFS: Cleanup demupltiplex thread exiting code
  CIFS: Move mid search to a separate function
  CIFS: Move RFC1002 check to a separate function
  CIFS: Simplify socket reading in demultiplex thread
  CIFS: Move buffer allocation to a separate function
  cifs: remove unneeded variable initialization in cifs_reconnect_tcon
  cifs: simplify refcounting for oplock breaks
  cifs: fix compiler warning in CIFSSMBQAllEAs
  cifs: fix name parsing in CIFSSMBQAllEAs
  cifs: don't start signing too early
  cifs: trivial: goto out here is unnecessary
  cifs: advertise the right receive buffer size to the server
parents 4a2d732f 762dfd10
...@@ -87,9 +87,15 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, ...@@ -87,9 +87,15 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
if ((cifs_pdu == NULL) || (server == NULL)) if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL; return -EINVAL;
if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
server->tcpStatus == CifsNeedNegotiate)
return rc; return rc;
if (!server->session_estab) {
strncpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
return rc;
}
cifs_pdu->Signature.Sequence.SequenceNumber = cifs_pdu->Signature.Sequence.SequenceNumber =
cpu_to_le32(server->sequence_number); cpu_to_le32(server->sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0; cifs_pdu->Signature.Sequence.Reserved = 0;
...@@ -178,9 +184,15 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, ...@@ -178,9 +184,15 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
if ((cifs_pdu == NULL) || (server == NULL)) if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL; return -EINVAL;
if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
server->tcpStatus == CifsNeedNegotiate)
return rc; return rc;
if (!server->session_estab) {
strncpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
return rc;
}
cifs_pdu->Signature.Sequence.SequenceNumber = cifs_pdu->Signature.Sequence.SequenceNumber =
cpu_to_le32(server->sequence_number); cpu_to_le32(server->sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0; cifs_pdu->Signature.Sequence.Reserved = 0;
......
...@@ -86,24 +86,6 @@ extern mempool_t *cifs_sm_req_poolp; ...@@ -86,24 +86,6 @@ extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
void
cifs_sb_active(struct super_block *sb)
{
struct cifs_sb_info *server = CIFS_SB(sb);
if (atomic_inc_return(&server->active) == 1)
atomic_inc(&sb->s_active);
}
void
cifs_sb_deactive(struct super_block *sb)
{
struct cifs_sb_info *server = CIFS_SB(sb);
if (atomic_dec_and_test(&server->active))
deactivate_super(sb);
}
static int static int
cifs_read_super(struct super_block *sb) cifs_read_super(struct super_block *sb)
{ {
......
...@@ -41,10 +41,6 @@ extern struct file_system_type cifs_fs_type; ...@@ -41,10 +41,6 @@ extern struct file_system_type cifs_fs_type;
extern const struct address_space_operations cifs_addr_ops; extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf; extern const struct address_space_operations cifs_addr_ops_smallbuf;
/* Functions related to super block operations */
extern void cifs_sb_active(struct super_block *sb);
extern void cifs_sb_deactive(struct super_block *sb);
/* Functions related to inodes */ /* Functions related to inodes */
extern const struct inode_operations cifs_dir_inode_ops; extern const struct inode_operations cifs_dir_inode_ops;
extern struct inode *cifs_root_iget(struct super_block *); extern struct inode *cifs_root_iget(struct super_block *);
......
...@@ -942,8 +942,6 @@ GLOBAL_EXTERN spinlock_t siduidlock; ...@@ -942,8 +942,6 @@ GLOBAL_EXTERN spinlock_t siduidlock;
GLOBAL_EXTERN spinlock_t sidgidlock; GLOBAL_EXTERN spinlock_t sidgidlock;
void cifs_oplock_break(struct work_struct *work); void cifs_oplock_break(struct work_struct *work);
void cifs_oplock_break_get(struct cifsFileInfo *cfile);
void cifs_oplock_break_put(struct cifsFileInfo *cfile);
extern const struct slow_work_ops cifs_oplock_break_ops; extern const struct slow_work_ops cifs_oplock_break_ops;
......
...@@ -107,7 +107,7 @@ static void mark_open_files_invalid(struct cifs_tcon *pTcon) ...@@ -107,7 +107,7 @@ static void mark_open_files_invalid(struct cifs_tcon *pTcon)
static int static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{ {
int rc = 0; int rc;
struct cifs_ses *ses; struct cifs_ses *ses;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct nls_table *nls_codepage; struct nls_table *nls_codepage;
...@@ -5720,6 +5720,7 @@ CIFSSMBQAllEAs(const int xid, struct cifs_tcon *tcon, ...@@ -5720,6 +5720,7 @@ CIFSSMBQAllEAs(const int xid, struct cifs_tcon *tcon,
char *temp_ptr; char *temp_ptr;
char *end_of_smb; char *end_of_smb;
__u16 params, byte_count, data_offset; __u16 params, byte_count, data_offset;
unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
cFYI(1, "In Query All EAs path %s", searchName); cFYI(1, "In Query All EAs path %s", searchName);
QAllEAsRetry: QAllEAsRetry:
...@@ -5837,7 +5838,8 @@ CIFSSMBQAllEAs(const int xid, struct cifs_tcon *tcon, ...@@ -5837,7 +5838,8 @@ CIFSSMBQAllEAs(const int xid, struct cifs_tcon *tcon,
} }
if (ea_name) { if (ea_name) {
if (strncmp(ea_name, temp_ptr, name_len) == 0) { if (ea_name_len == name_len &&
strncmp(ea_name, temp_ptr, name_len) == 0) {
temp_ptr += name_len + 1; temp_ptr += name_len + 1;
rc = value_len; rc = value_len;
if (buf_size == 0) if (buf_size == 0)
......
...@@ -319,344 +319,221 @@ cifs_echo_request(struct work_struct *work) ...@@ -319,344 +319,221 @@ cifs_echo_request(struct work_struct *work)
queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL);
} }
static int static bool
cifs_demultiplex_thread(void *p) allocate_buffers(char **bigbuf, char **smallbuf, unsigned int size,
bool is_large_buf)
{ {
int length; char *bbuf = *bigbuf, *sbuf = *smallbuf;
struct TCP_Server_Info *server = p;
unsigned int pdu_length, total_read;
struct smb_hdr *smb_buffer = NULL;
struct smb_hdr *bigbuf = NULL;
struct smb_hdr *smallbuf = NULL;
struct msghdr smb_msg;
struct kvec iov;
struct socket *csocket = server->ssocket;
struct list_head *tmp, *tmp2;
struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry;
char temp;
bool isLargeBuf = false;
bool isMultiRsp;
int reconnect;
current->flags |= PF_MEMALLOC;
cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
length = atomic_inc_return(&tcpSesAllocCount);
if (length > 1)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL);
set_freezable(); if (bbuf == NULL) {
while (server->tcpStatus != CifsExiting) { bbuf = (char *)cifs_buf_get();
if (try_to_freeze()) if (!bbuf) {
continue;
if (bigbuf == NULL) {
bigbuf = cifs_buf_get();
if (!bigbuf) {
cERROR(1, "No memory for large SMB response"); cERROR(1, "No memory for large SMB response");
msleep(3000); msleep(3000);
/* retry will check if exiting */ /* retry will check if exiting */
continue; return false;
} }
} else if (isLargeBuf) { } else if (is_large_buf) {
/* we are reusing a dirty large buf, clear its start */ /* we are reusing a dirty large buf, clear its start */
memset(bigbuf, 0, sizeof(struct smb_hdr)); memset(bbuf, 0, size);
} }
if (smallbuf == NULL) { if (sbuf == NULL) {
smallbuf = cifs_small_buf_get(); sbuf = (char *)cifs_small_buf_get();
if (!smallbuf) { if (!sbuf) {
cERROR(1, "No memory for SMB response"); cERROR(1, "No memory for SMB response");
msleep(1000); msleep(1000);
/* retry will check if exiting */ /* retry will check if exiting */
continue; return false;
} }
/* beginning of smb buffer is cleared in our buf_get */ /* beginning of smb buffer is cleared in our buf_get */
} else /* if existing small buf clear beginning */ } else {
memset(smallbuf, 0, sizeof(struct smb_hdr)); /* if existing small buf clear beginning */
memset(sbuf, 0, size);
}
isLargeBuf = false; *bigbuf = bbuf;
isMultiRsp = false; *smallbuf = sbuf;
smb_buffer = smallbuf;
iov.iov_base = smb_buffer;
iov.iov_len = 4;
smb_msg.msg_control = NULL;
smb_msg.msg_controllen = 0;
pdu_length = 4; /* enough to get RFC1001 header */
incomplete_rcv: return true;
if (echo_retries > 0 && server->tcpStatus == CifsGood && }
time_after(jiffies, server->lstrp +
(echo_retries * SMB_ECHO_INTERVAL))) {
cERROR(1, "Server %s has not responded in %d seconds. "
"Reconnecting...", server->hostname,
(echo_retries * SMB_ECHO_INTERVAL / HZ));
cifs_reconnect(server);
csocket = server->ssocket;
wake_up(&server->response_q);
continue;
}
length = static int
kernel_recvmsg(csocket, &smb_msg, read_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg,
&iov, 1, pdu_length, 0 /* BB other flags? */); struct kvec *iov, unsigned int to_read,
unsigned int *ptotal_read, bool is_header_read)
{
int length, rc = 0;
unsigned int total_read;
char *buf = iov->iov_base;
for (total_read = 0; total_read < to_read; total_read += length) {
length = kernel_recvmsg(server->ssocket, smb_msg, iov, 1,
to_read - total_read, 0);
if (server->tcpStatus == CifsExiting) { if (server->tcpStatus == CifsExiting) {
/* then will exit */
rc = 2;
break; break;
} else if (server->tcpStatus == CifsNeedReconnect) { } else if (server->tcpStatus == CifsNeedReconnect) {
cFYI(1, "Reconnect after server stopped responding");
cifs_reconnect(server); cifs_reconnect(server);
cFYI(1, "call to reconnect done"); /* Reconnect wakes up rspns q */
csocket = server->ssocket; /* Now we will reread sock */
continue; rc = 1;
break;
} else if (length == -ERESTARTSYS || } else if (length == -ERESTARTSYS ||
length == -EAGAIN || length == -EAGAIN ||
length == -EINTR) { length == -EINTR) {
msleep(1); /* minimum sleep to prevent looping /*
allowing socket to clear and app threads to set * Minimum sleep to prevent looping, allowing socket
tcpStatus CifsNeedReconnect if server hung */ * to clear and app threads to set tcpStatus
if (pdu_length < 4) { * CifsNeedReconnect if server hung.
iov.iov_base = (4 - pdu_length) + */
(char *)smb_buffer; usleep_range(1000, 2000);
iov.iov_len = pdu_length; length = 0;
smb_msg.msg_control = NULL; if (!is_header_read)
smb_msg.msg_controllen = 0;
goto incomplete_rcv;
} else
continue; continue;
/* Special handling for header read */
if (total_read) {
iov->iov_base = (to_read - total_read) +
buf;
iov->iov_len = to_read - total_read;
smb_msg->msg_control = NULL;
smb_msg->msg_controllen = 0;
rc = 3;
} else
rc = 1;
break;
} else if (length <= 0) { } else if (length <= 0) {
cFYI(1, "Reconnect after unexpected peek error %d", cERROR(1, "Received no data, expecting %d",
length); to_read - total_read);
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket; rc = 1;
wake_up(&server->response_q); break;
continue; }
} else if (length < pdu_length) {
cFYI(1, "requested %d bytes but only got %d bytes",
pdu_length, length);
pdu_length -= length;
msleep(1);
goto incomplete_rcv;
} }
/* The right amount was read from socket - 4 bytes */ *ptotal_read = total_read;
/* so we can now interpret the length field */ return rc;
}
/* the first byte big endian of the length field,
is actually not part of the length but the type
with the most common, zero, as regular data */
temp = *((char *) smb_buffer);
/* Note that FC 1001 length is big endian on the wire,
but we convert it here so it is always manipulated
as host byte order */
pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
cFYI(1, "rfc1002 length 0x%x", pdu_length+4); static bool
check_rfc1002_header(struct TCP_Server_Info *server, char *buf)
{
char temp = *buf;
unsigned int pdu_length = be32_to_cpu(
((struct smb_hdr *)buf)->smb_buf_length);
/*
* The first byte big endian of the length field,
* is actually not part of the length but the type
* with the most common, zero, as regular data.
*/
if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) { if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
continue; return false;
} else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
cFYI(1, "Good RFC 1002 session rsp"); cFYI(1, "Good RFC 1002 session rsp");
continue; return false;
} else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
/* we get this from Windows 98 instead of /*
an error on SMB negprot response */ * We get this from Windows 98 instead of an error on
* SMB negprot response.
*/
cFYI(1, "Negative RFC1002 Session Response Error 0x%x)", cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
pdu_length); pdu_length);
/* give server a second to clean up */ /* give server a second to clean up */
msleep(1000); msleep(1000);
/* always try 445 first on reconnect since we get NACK /*
* Always try 445 first on reconnect since we get NACK
* on some if we ever connected to port 139 (the NACK * on some if we ever connected to port 139 (the NACK
* is since we do not begin with RFC1001 session * is since we do not begin with RFC1001 session
* initialize frame) * initialize frame).
*/ */
cifs_set_port((struct sockaddr *) cifs_set_port((struct sockaddr *)
&server->dstaddr, CIFS_PORT); &server->dstaddr, CIFS_PORT);
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket;
wake_up(&server->response_q); wake_up(&server->response_q);
continue; return false;
} else if (temp != (char) 0) { } else if (temp != (char) 0) {
cERROR(1, "Unknown RFC 1002 frame"); cERROR(1, "Unknown RFC 1002 frame");
cifs_dump_mem(" Received Data: ", (char *)smb_buffer, cifs_dump_mem(" Received Data: ", buf, 4);
length);
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket; return false;
continue;
} }
/* else we have an SMB response */ /* else we have an SMB response */
if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) || if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
(pdu_length < sizeof(struct smb_hdr) - 1 - 4)) { (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
cERROR(1, "Invalid size SMB length %d pdu_length %d", cERROR(1, "Invalid size SMB length %d pdu_length %d",
length, pdu_length+4); 4, pdu_length+4);
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket;
wake_up(&server->response_q); wake_up(&server->response_q);
continue; return false;
} }
/* else length ok */ return true;
reconnect = 0; }
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
isLargeBuf = true;
memcpy(bigbuf, smallbuf, 4);
smb_buffer = bigbuf;
}
length = 0;
iov.iov_base = 4 + (char *)smb_buffer;
iov.iov_len = pdu_length;
for (total_read = 0; total_read < pdu_length;
total_read += length) {
length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
pdu_length - total_read, 0);
if (server->tcpStatus == CifsExiting) {
/* then will exit */
reconnect = 2;
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
cifs_reconnect(server);
csocket = server->ssocket;
/* Reconnect wakes up rspns q */
/* Now we will reread sock */
reconnect = 1;
break;
} else if (length == -ERESTARTSYS ||
length == -EAGAIN ||
length == -EINTR) {
msleep(1); /* minimum sleep to prevent looping,
allowing socket to clear and app
threads to set tcpStatus
CifsNeedReconnect if server hung*/
length = 0;
continue;
} else if (length <= 0) {
cERROR(1, "Received no data, expecting %d",
pdu_length - total_read);
cifs_reconnect(server);
csocket = server->ssocket;
reconnect = 1;
break;
}
}
if (reconnect == 2)
break;
else if (reconnect == 1)
continue;
total_read += 4; /* account for rfc1002 hdr */
dump_smb(smb_buffer, total_read);
/*
* We know that we received enough to get to the MID as we
* checked the pdu_length earlier. Now check to see
* if the rest of the header is OK. We borrow the length
* var for the rest of the loop to avoid a new stack var.
*
* 48 bytes is enough to display the header and a little bit
* into the payload for debugging purposes.
*/
length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
if (length != 0)
cifs_dump_mem("Bad SMB: ", smb_buffer,
min_t(unsigned int, total_read, 48));
mid_entry = NULL; static struct mid_q_entry *
server->lstrp = jiffies; find_cifs_mid(struct TCP_Server_Info *server, struct smb_hdr *buf,
int *length, bool is_large_buf, bool *is_multi_rsp, char **bigbuf)
{
struct mid_q_entry *mid = NULL, *tmp_mid, *ret = NULL;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { list_for_each_entry_safe(mid, tmp_mid, &server->pending_mid_q, qhead) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid->mid != buf->Mid ||
mid->midState != MID_REQUEST_SUBMITTED ||
if (mid_entry->mid != smb_buffer->Mid || mid->command != buf->Command)
mid_entry->midState != MID_REQUEST_SUBMITTED ||
mid_entry->command != smb_buffer->Command) {
mid_entry = NULL;
continue; continue;
}
if (length == 0 && if (*length == 0 && check2ndT2(buf, server->maxBuf) > 0) {
check2ndT2(smb_buffer, server->maxBuf) > 0) {
/* We have a multipart transact2 resp */ /* We have a multipart transact2 resp */
isMultiRsp = true; *is_multi_rsp = true;
if (mid_entry->resp_buf) { if (mid->resp_buf) {
/* merge response - fix up 1st*/ /* merge response - fix up 1st*/
length = coalesce_t2(smb_buffer, *length = coalesce_t2(buf, mid->resp_buf);
mid_entry->resp_buf); if (*length > 0) {
if (length > 0) { *length = 0;
length = 0; mid->multiRsp = true;
mid_entry->multiRsp = true;
break; break;
} else { }
/* all parts received or /* All parts received or packet is malformed. */
* packet is malformed mid->multiEnd = true;
*/
mid_entry->multiEnd = true;
goto multi_t2_fnd; goto multi_t2_fnd;
} }
} else { if (!is_large_buf) {
if (!isLargeBuf) { /*FIXME: switch to already allocated largebuf?*/
/* cERROR(1, "1st trans2 resp needs bigbuf");
* FIXME: switch to already
* allocated largebuf?
*/
cERROR(1, "1st trans2 resp "
"needs bigbuf");
} else { } else {
/* Have first buffer */ /* Have first buffer */
mid_entry->resp_buf = mid->resp_buf = buf;
smb_buffer; mid->largeBuf = true;
mid_entry->largeBuf = true; *bigbuf = NULL;
bigbuf = NULL;
}
} }
break; break;
} }
mid_entry->resp_buf = smb_buffer; mid->resp_buf = buf;
mid_entry->largeBuf = isLargeBuf; mid->largeBuf = is_large_buf;
multi_t2_fnd: multi_t2_fnd:
if (length == 0) if (*length == 0)
mid_entry->midState = MID_RESPONSE_RECEIVED; mid->midState = MID_RESPONSE_RECEIVED;
else else
mid_entry->midState = MID_RESPONSE_MALFORMED; mid->midState = MID_RESPONSE_MALFORMED;
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
mid_entry->when_received = jiffies; mid->when_received = jiffies;
#endif #endif
list_del_init(&mid_entry->qhead); list_del_init(&mid->qhead);
ret = mid;
break; break;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
if (mid_entry != NULL) { return ret;
mid_entry->callback(mid_entry); }
/* Was previous buf put in mpx struct for multi-rsp? */
if (!isMultiRsp) {
/* smb buffer will be freed by user thread */
if (isLargeBuf)
bigbuf = NULL;
else
smallbuf = NULL;
}
} else if (length != 0) {
/* response sanity checks failed */
continue;
} else if (!is_valid_oplock_break(smb_buffer, server) &&
!isMultiRsp) {
cERROR(1, "No task to wake, unknown frame received! "
"NumMids %d", atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
sizeof(struct smb_hdr));
#ifdef CONFIG_CIFS_DEBUG2
cifs_dump_detail(smb_buffer);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
} static void clean_demultiplex_info(struct TCP_Server_Info *server)
} /* end while !EXITING */ {
int length;
/* take it off the list, if it's not already */ /* take it off the list, if it's not already */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
...@@ -668,35 +545,39 @@ cifs_demultiplex_thread(void *p) ...@@ -668,35 +545,39 @@ cifs_demultiplex_thread(void *p)
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
wake_up_all(&server->response_q); wake_up_all(&server->response_q);
/* check if we have blocked requests that need to free */ /*
/* Note that cifs_max_pending is normally 50, but * Check if we have blocked requests that need to free. Note that
can be set at module install time to as little as two */ * cifs_max_pending is normally 50, but can be set at module install
* time to as little as two.
*/
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (atomic_read(&server->inFlight) >= cifs_max_pending) if (atomic_read(&server->inFlight) >= cifs_max_pending)
atomic_set(&server->inFlight, cifs_max_pending - 1); atomic_set(&server->inFlight, cifs_max_pending - 1);
/* We do not want to set the max_pending too low or we /*
could end up with the counter going negative */ * We do not want to set the max_pending too low or we could end up
* with the counter going negative.
*/
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
/* Although there should not be any requests blocked on /*
this queue it can not hurt to be paranoid and try to wake up requests * Although there should not be any requests blocked on this queue it
that may haven been blocked when more than 50 at time were on the wire * can not hurt to be paranoid and try to wake up requests that may
to the same server - they now will see the session is in exit state * haven been blocked when more than 50 at time were on the wire to the
and get out of SendReceive. */ * same server - they now will see the session is in exit state and get
* out of SendReceive.
*/
wake_up_all(&server->request_q); wake_up_all(&server->request_q);
/* give those requests time to exit */ /* give those requests time to exit */
msleep(125); msleep(125);
if (server->ssocket) { if (server->ssocket) {
sock_release(csocket); sock_release(server->ssocket);
server->ssocket = NULL; server->ssocket = NULL;
} }
/* buffer usually freed in free_mid - need to free it here on exit */
cifs_buf_release(bigbuf);
if (smallbuf) /* no sense logging a debug message if NULL */
cifs_small_buf_release(smallbuf);
if (!list_empty(&server->pending_mid_q)) { if (!list_empty(&server->pending_mid_q)) {
struct list_head dispose_list; struct list_head dispose_list;
struct mid_q_entry *mid_entry;
struct list_head *tmp, *tmp2;
INIT_LIST_HEAD(&dispose_list); INIT_LIST_HEAD(&dispose_list);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
...@@ -720,26 +601,184 @@ cifs_demultiplex_thread(void *p) ...@@ -720,26 +601,184 @@ cifs_demultiplex_thread(void *p)
} }
if (!list_empty(&server->pending_mid_q)) { if (!list_empty(&server->pending_mid_q)) {
/* mpx threads have not exited yet give them /*
at least the smb send timeout time for long ops */ * mpx threads have not exited yet give them at least the smb
/* due to delays on oplock break requests, we need * send timeout time for long ops.
to wait at least 45 seconds before giving up *
on a request getting a response and going ahead * Due to delays on oplock break requests, we need to wait at
and killing cifsd */ * least 45 seconds before giving up on a request getting a
* response and going ahead and killing cifsd.
*/
cFYI(1, "Wait for exit from demultiplex thread"); cFYI(1, "Wait for exit from demultiplex thread");
msleep(46000); msleep(46000);
/* if threads still have not exited they are probably never /*
coming home not much else we can do but free the memory */ * If threads still have not exited they are probably never
* coming home not much else we can do but free the memory.
*/
} }
kfree(server->hostname); kfree(server->hostname);
task_to_wake = xchg(&server->tsk, NULL);
kfree(server); kfree(server);
length = atomic_dec_return(&tcpSesAllocCount); length = atomic_dec_return(&tcpSesAllocCount);
if (length > 0) if (length > 0)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv, mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL); GFP_KERNEL);
}
static int
cifs_demultiplex_thread(void *p)
{
int length;
struct TCP_Server_Info *server = p;
unsigned int pdu_length, total_read;
char *buf = NULL, *bigbuf = NULL, *smallbuf = NULL;
struct smb_hdr *smb_buffer = NULL;
struct msghdr smb_msg;
struct kvec iov;
struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry;
bool isLargeBuf = false;
bool isMultiRsp = false;
int rc;
current->flags |= PF_MEMALLOC;
cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
length = atomic_inc_return(&tcpSesAllocCount);
if (length > 1)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL);
set_freezable();
while (server->tcpStatus != CifsExiting) {
if (try_to_freeze())
continue;
if (!allocate_buffers(&bigbuf, &smallbuf,
sizeof(struct smb_hdr), isLargeBuf))
continue;
isLargeBuf = false;
isMultiRsp = false;
smb_buffer = (struct smb_hdr *)smallbuf;
buf = smallbuf;
iov.iov_base = buf;
iov.iov_len = 4;
smb_msg.msg_control = NULL;
smb_msg.msg_controllen = 0;
pdu_length = 4; /* enough to get RFC1001 header */
incomplete_rcv:
if (echo_retries > 0 && server->tcpStatus == CifsGood &&
time_after(jiffies, server->lstrp +
(echo_retries * SMB_ECHO_INTERVAL))) {
cERROR(1, "Server %s has not responded in %d seconds. "
"Reconnecting...", server->hostname,
(echo_retries * SMB_ECHO_INTERVAL / HZ));
cifs_reconnect(server);
wake_up(&server->response_q);
continue;
}
rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
&total_read, true /* header read */);
if (rc == 3)
goto incomplete_rcv;
else if (rc == 2)
break;
else if (rc == 1)
continue;
/*
* The right amount was read from socket - 4 bytes,
* so we can now interpret the length field.
*/
/*
* Note that RFC 1001 length is big endian on the wire,
* but we convert it here so it is always manipulated
* as host byte order.
*/
pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
cFYI(1, "rfc1002 length 0x%x", pdu_length+4);
if (!check_rfc1002_header(server, buf))
continue;
/* else length ok */
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
isLargeBuf = true;
memcpy(bigbuf, smallbuf, 4);
smb_buffer = (struct smb_hdr *)bigbuf;
buf = bigbuf;
}
iov.iov_base = 4 + buf;
iov.iov_len = pdu_length;
rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
&total_read, false);
if (rc == 2)
break;
else if (rc == 1)
continue;
total_read += 4; /* account for rfc1002 hdr */
dump_smb(smb_buffer, total_read);
/*
* We know that we received enough to get to the MID as we
* checked the pdu_length earlier. Now check to see
* if the rest of the header is OK. We borrow the length
* var for the rest of the loop to avoid a new stack var.
*
* 48 bytes is enough to display the header and a little bit
* into the payload for debugging purposes.
*/
length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
if (length != 0)
cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, total_read, 48));
server->lstrp = jiffies;
mid_entry = find_cifs_mid(server, smb_buffer, &length,
isLargeBuf, &isMultiRsp, &bigbuf);
if (mid_entry != NULL) {
mid_entry->callback(mid_entry);
/* Was previous buf put in mpx struct for multi-rsp? */
if (!isMultiRsp) {
/* smb buffer will be freed by user thread */
if (isLargeBuf)
bigbuf = NULL;
else
smallbuf = NULL;
}
} else if (length != 0) {
/* response sanity checks failed */
continue;
} else if (!is_valid_oplock_break(smb_buffer, server) &&
!isMultiRsp) {
cERROR(1, "No task to wake, unknown frame received! "
"NumMids %d", atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", buf,
sizeof(struct smb_hdr));
#ifdef CONFIG_CIFS_DEBUG2
cifs_dump_detail(smb_buffer);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
}
} /* end while !EXITING */
/* buffer usually freed in free_mid - need to free it here on exit */
cifs_buf_release(bigbuf);
if (smallbuf) /* no sense logging a debug message if NULL */
cifs_small_buf_release(smallbuf);
task_to_wake = xchg(&server->tsk, NULL);
clean_demultiplex_info(server);
/* if server->tsk was NULL then wait for a signal before exiting */ /* if server->tsk was NULL then wait for a signal before exiting */
if (!task_to_wake) { if (!task_to_wake) {
...@@ -3193,15 +3232,9 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) ...@@ -3193,15 +3232,9 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
else else
cifs_put_tcp_session(srvTcp); cifs_put_tcp_session(srvTcp);
bdi_destroy(&cifs_sb->bdi); bdi_destroy(&cifs_sb->bdi);
goto out;
} }
/* volume_info->password is freed above when existing session found
(in which case it is not needed anymore) but when new sesion is created
the password ptr is put in the new session structure (in which case the
password will be freed at unmount time) */
out: out:
/* zero out password before freeing */
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
......
...@@ -314,6 +314,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -314,6 +314,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
cancel_work_sync(&cifs_file->oplock_break);
if (!tcon->need_reconnect && !cifs_file->invalidHandle) { if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
int xid, rc; int xid, rc;
...@@ -2418,31 +2420,6 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -2418,31 +2420,6 @@ void cifs_oplock_break(struct work_struct *work)
cinode->clientCanCacheRead ? 1 : 0); cinode->clientCanCacheRead ? 1 : 0);
cFYI(1, "Oplock release rc = %d", rc); cFYI(1, "Oplock release rc = %d", rc);
} }
/*
* We might have kicked in before is_valid_oplock_break()
* finished grabbing reference for us. Make sure it's done by
* waiting for cifs_file_list_lock.
*/
spin_lock(&cifs_file_list_lock);
spin_unlock(&cifs_file_list_lock);
cifs_oplock_break_put(cfile);
}
/* must be called while holding cifs_file_list_lock */
void cifs_oplock_break_get(struct cifsFileInfo *cfile)
{
cifs_sb_active(cfile->dentry->d_sb);
cifsFileInfo_get(cfile);
}
void cifs_oplock_break_put(struct cifsFileInfo *cfile)
{
struct super_block *sb = cfile->dentry->d_sb;
cifsFileInfo_put(cfile);
cifs_sb_deactive(sb);
} }
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
......
...@@ -585,15 +585,8 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -585,15 +585,8 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
cifs_set_oplock_level(pCifsInode, cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0); pSMB->OplockLevel ? OPLOCK_READ : 0);
/* queue_work(system_nrt_wq,
* cifs_oplock_break_put() can't be called &netfile->oplock_break);
* from here. Get reference after queueing
* succeeded. cifs_oplock_break() will
* synchronize using cifs_file_list_lock.
*/
if (queue_work(system_nrt_wq,
&netfile->oplock_break))
cifs_oplock_break_get(netfile);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
......
...@@ -124,7 +124,8 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) ...@@ -124,7 +124,8 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
/* that we use in next few lines */ /* that we use in next few lines */
/* Note that header is initialized to zero in header_assemble */ /* Note that header is initialized to zero in header_assemble */
pSMB->req.AndXCommand = 0xFF; pSMB->req.AndXCommand = 0xFF;
pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, CIFSMaxBufSize - 4,
USHRT_MAX));
pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
pSMB->req.VcNumber = get_next_vcnum(ses); pSMB->req.VcNumber = get_next_vcnum(ses);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment