Commit 7c2614bf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.0-rc3-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb3 fixes from Steve French:
 "A set of small smb3 fixes, some fixing various crediting issues
  discovered during xfstest runs, five for stable"

* tag '5.0-rc3-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: print CIFSMaxBufSize as part of /proc/fs/cifs/DebugData
  smb3: add credits we receive from oplock/break PDUs
  CIFS: Fix mounts if the client is low on credits
  CIFS: Do not assume one credit for async responses
  CIFS: Fix credit calculations in compound mid callback
  CIFS: Fix credit calculation for encrypted reads with errors
  CIFS: Fix credits calculations for reads with errors
  CIFS: Do not reconnect TCP session in add_credits()
  smb3: Cleanup license mess
  CIFS: Fix possible hang during async MTU reads and writes
  cifs: fix memory leak of an allocated cifs_ntsd structure
parents 2580acb2 a5f1a81f
...@@ -252,6 +252,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -252,6 +252,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, ",ACL"); seq_printf(m, ",ACL");
#endif #endif
seq_putc(m, '\n'); seq_putc(m, '\n');
seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
seq_printf(m, "Servers:"); seq_printf(m, "Servers:");
......
...@@ -1549,18 +1549,26 @@ cifs_discard_remaining_data(struct TCP_Server_Info *server) ...@@ -1549,18 +1549,26 @@ cifs_discard_remaining_data(struct TCP_Server_Info *server)
} }
static int static int
cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) __cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid,
bool malformed)
{ {
int length; int length;
struct cifs_readdata *rdata = mid->callback_data;
length = cifs_discard_remaining_data(server); length = cifs_discard_remaining_data(server);
dequeue_mid(mid, rdata->result); dequeue_mid(mid, malformed);
mid->resp_buf = server->smallbuf; mid->resp_buf = server->smallbuf;
server->smallbuf = NULL; server->smallbuf = NULL;
return length; return length;
} }
static int
cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
struct cifs_readdata *rdata = mid->callback_data;
return __cifs_readv_discard(server, mid, rdata->result);
}
int int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
...@@ -1602,12 +1610,23 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1602,12 +1610,23 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return -1; return -1;
} }
/* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[1].iov_len = server->total_read - 4;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
rdata->iov[1].iov_base, rdata->iov[1].iov_len);
/* Was the SMB read successful? */ /* Was the SMB read successful? */
rdata->result = server->ops->map_error(buf, false); rdata->result = server->ops->map_error(buf, false);
if (rdata->result != 0) { if (rdata->result != 0) {
cifs_dbg(FYI, "%s: server returned error %d\n", cifs_dbg(FYI, "%s: server returned error %d\n",
__func__, rdata->result); __func__, rdata->result);
return cifs_readv_discard(server, mid); /* normal error on read response */
return __cifs_readv_discard(server, mid, false);
} }
/* Is there enough to get to the rest of the READ_RSP header? */ /* Is there enough to get to the rest of the READ_RSP header? */
...@@ -1651,14 +1670,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1651,14 +1670,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
server->total_read += length; server->total_read += length;
} }
/* set up first iov for signature check */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[1].iov_len = server->total_read - 4;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n",
rdata->iov[0].iov_base, server->total_read);
/* how much data is in the response? */ /* how much data is in the response? */
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
use_rdma_mr = rdata->mr; use_rdma_mr = rdata->mr;
......
...@@ -720,6 +720,21 @@ server_unresponsive(struct TCP_Server_Info *server) ...@@ -720,6 +720,21 @@ server_unresponsive(struct TCP_Server_Info *server)
return false; return false;
} }
static inline bool
zero_credits(struct TCP_Server_Info *server)
{
int val;
spin_lock(&server->req_lock);
val = server->credits + server->echo_credits + server->oplock_credits;
if (server->in_flight == 0 && val == 0) {
spin_unlock(&server->req_lock);
return true;
}
spin_unlock(&server->req_lock);
return false;
}
static int static int
cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
{ {
...@@ -732,6 +747,12 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) ...@@ -732,6 +747,12 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
for (total_read = 0; msg_data_left(smb_msg); total_read += length) { for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
try_to_freeze(); try_to_freeze();
/* reconnect if no credits and no requests in flight */
if (zero_credits(server)) {
cifs_reconnect(server);
return -ECONNABORTED;
}
if (server_unresponsive(server)) if (server_unresponsive(server))
return -ECONNABORTED; return -ECONNABORTED;
if (cifs_rdma_enabled(server) && server->smbd_conn) if (cifs_rdma_enabled(server) && server->smbd_conn)
......
...@@ -293,6 +293,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -293,6 +293,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
int rc; int rc;
struct smb2_file_all_info *smb2_data; struct smb2_file_all_info *smb2_data;
__u32 create_options = 0; __u32 create_options = 0;
struct cifs_fid fid;
bool no_cached_open = tcon->nohandlecache;
*adjust_tz = false; *adjust_tz = false;
*symlink = false; *symlink = false;
...@@ -301,6 +303,21 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -301,6 +303,21 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
GFP_KERNEL); GFP_KERNEL);
if (smb2_data == NULL) if (smb2_data == NULL)
return -ENOMEM; return -ENOMEM;
/* If it is a root and its handle is cached then use it */
if (!strlen(full_path) && !no_cached_open) {
rc = open_shroot(xid, tcon, &fid);
if (rc)
goto out;
rc = SMB2_query_info(xid, tcon, fid.persistent_fid,
fid.volatile_fid, smb2_data);
close_shroot(&tcon->crfid);
if (rc)
goto out;
move_smb2_info_to_cifs(data, smb2_data);
goto out;
}
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
......
...@@ -648,6 +648,13 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -648,6 +648,13 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK) if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
return false; return false;
if (rsp->sync_hdr.CreditRequest) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(rsp->sync_hdr.CreditRequest);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
}
if (rsp->StructureSize != if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44) if (le16_to_cpu(rsp->StructureSize) == 44)
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "cifs_ioctl.h" #include "cifs_ioctl.h"
#include "smbdirect.h" #include "smbdirect.h"
/* Change credits for different ops and return the total number of credits */
static int static int
change_conf(struct TCP_Server_Info *server) change_conf(struct TCP_Server_Info *server)
{ {
...@@ -41,17 +42,15 @@ change_conf(struct TCP_Server_Info *server) ...@@ -41,17 +42,15 @@ change_conf(struct TCP_Server_Info *server)
server->oplock_credits = server->echo_credits = 0; server->oplock_credits = server->echo_credits = 0;
switch (server->credits) { switch (server->credits) {
case 0: case 0:
return -1; return 0;
case 1: case 1:
server->echoes = false; server->echoes = false;
server->oplocks = false; server->oplocks = false;
cifs_dbg(VFS, "disabling echoes and oplocks\n");
break; break;
case 2: case 2:
server->echoes = true; server->echoes = true;
server->oplocks = false; server->oplocks = false;
server->echo_credits = 1; server->echo_credits = 1;
cifs_dbg(FYI, "disabling oplocks\n");
break; break;
default: default:
server->echoes = true; server->echoes = true;
...@@ -64,14 +63,15 @@ change_conf(struct TCP_Server_Info *server) ...@@ -64,14 +63,15 @@ change_conf(struct TCP_Server_Info *server)
server->echo_credits = 1; server->echo_credits = 1;
} }
server->credits -= server->echo_credits + server->oplock_credits; server->credits -= server->echo_credits + server->oplock_credits;
return 0; return server->credits + server->echo_credits + server->oplock_credits;
} }
static void static void
smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
const int optype) const int optype)
{ {
int *val, rc = 0; int *val, rc = -1;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
val = server->ops->get_credits_field(server, optype); val = server->ops->get_credits_field(server, optype);
...@@ -101,8 +101,26 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, ...@@ -101,8 +101,26 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
} }
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
if (rc)
cifs_reconnect(server); if (server->tcpStatus == CifsNeedReconnect)
return;
switch (rc) {
case -1:
/* change_conf hasn't been executed */
break;
case 0:
cifs_dbg(VFS, "Possible client or server bug - zero credits\n");
break;
case 1:
cifs_dbg(VFS, "disabling echoes and oplocks\n");
break;
case 2:
cifs_dbg(FYI, "disabling oplocks\n");
break;
default:
cifs_dbg(FYI, "add %u credits total=%d\n", add, rc);
}
} }
static void static void
...@@ -136,7 +154,11 @@ smb2_get_credits(struct mid_q_entry *mid) ...@@ -136,7 +154,11 @@ smb2_get_credits(struct mid_q_entry *mid)
{ {
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)mid->resp_buf; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)mid->resp_buf;
if (mid->mid_state == MID_RESPONSE_RECEIVED
|| mid->mid_state == MID_RESPONSE_MALFORMED)
return le16_to_cpu(shdr->CreditRequest); return le16_to_cpu(shdr->CreditRequest);
return 0;
} }
static int static int
...@@ -165,14 +187,14 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, ...@@ -165,14 +187,14 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
scredits = server->credits; scredits = server->credits;
/* can deadlock with reopen */ /* can deadlock with reopen */
if (scredits == 1) { if (scredits <= 8) {
*num = SMB2_MAX_BUFFER_SIZE; *num = SMB2_MAX_BUFFER_SIZE;
*credits = 0; *credits = 0;
break; break;
} }
/* leave one credit for a possible reopen */ /* leave some credits for reopen and other ops */
scredits--; scredits -= 8;
*num = min_t(unsigned int, size, *num = min_t(unsigned int, size,
scredits * SMB2_MAX_BUFFER_SIZE); scredits * SMB2_MAX_BUFFER_SIZE);
...@@ -3189,11 +3211,23 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, ...@@ -3189,11 +3211,23 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
server->ops->is_status_pending(buf, server, 0)) server->ops->is_status_pending(buf, server, 0))
return -1; return -1;
rdata->result = server->ops->map_error(buf, false); /* set up first two iov to get credits */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[1].iov_len =
min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
rdata->iov[1].iov_base, rdata->iov[1].iov_len);
rdata->result = server->ops->map_error(buf, true);
if (rdata->result != 0) { if (rdata->result != 0) {
cifs_dbg(FYI, "%s: server returned error %d\n", cifs_dbg(FYI, "%s: server returned error %d\n",
__func__, rdata->result); __func__, rdata->result);
dequeue_mid(mid, rdata->result); /* normal error on read response */
dequeue_mid(mid, false);
return 0; return 0;
} }
...@@ -3266,14 +3300,6 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, ...@@ -3266,14 +3300,6 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
return 0; return 0;
} }
/* set up first iov for signature check */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[1].iov_len = server->vals->read_rsp_size - 4;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, server->vals->read_rsp_size);
length = rdata->copy_into_pages(server, rdata, &iter); length = rdata->copy_into_pages(server, rdata, &iter);
kfree(bvec); kfree(bvec);
......
...@@ -2816,6 +2816,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2816,6 +2816,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
int resp_buftype = CIFS_NO_BUFFER; int resp_buftype = CIFS_NO_BUFFER;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
int flags = 0; int flags = 0;
bool allocated = false;
cifs_dbg(FYI, "Query Info\n"); cifs_dbg(FYI, "Query Info\n");
...@@ -2855,14 +2856,21 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2855,14 +2856,21 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
"Error %d allocating memory for acl\n", "Error %d allocating memory for acl\n",
rc); rc);
*dlen = 0; *dlen = 0;
rc = -ENOMEM;
goto qinf_exit; goto qinf_exit;
} }
allocated = true;
} }
} }
rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), le32_to_cpu(rsp->OutputBufferLength),
&rsp_iov, min_len, *data); &rsp_iov, min_len, *data);
if (rc && allocated) {
kfree(*data);
*data = NULL;
*dlen = 0;
}
qinf_exit: qinf_exit:
SMB2_query_info_free(&rqst); SMB2_query_info_free(&rqst);
...@@ -2916,9 +2924,10 @@ smb2_echo_callback(struct mid_q_entry *mid) ...@@ -2916,9 +2924,10 @@ smb2_echo_callback(struct mid_q_entry *mid)
{ {
struct TCP_Server_Info *server = mid->callback_data; struct TCP_Server_Info *server = mid->callback_data;
struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
unsigned int credits_received = 1; unsigned int credits_received = 0;
if (mid->mid_state == MID_RESPONSE_RECEIVED) if (mid->mid_state == MID_RESPONSE_RECEIVED
|| mid->mid_state == MID_RESPONSE_MALFORMED)
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest); credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
DeleteMidQEntry(mid); DeleteMidQEntry(mid);
...@@ -3175,7 +3184,7 @@ smb2_readv_callback(struct mid_q_entry *mid) ...@@ -3175,7 +3184,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct smb2_sync_hdr *shdr = struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)rdata->iov[0].iov_base; (struct smb2_sync_hdr *)rdata->iov[0].iov_base;
unsigned int credits_received = 1; unsigned int credits_received = 0;
struct smb_rqst rqst = { .rq_iov = rdata->iov, struct smb_rqst rqst = { .rq_iov = rdata->iov,
.rq_nvec = 2, .rq_nvec = 2,
.rq_pages = rdata->pages, .rq_pages = rdata->pages,
...@@ -3214,6 +3223,9 @@ smb2_readv_callback(struct mid_q_entry *mid) ...@@ -3214,6 +3223,9 @@ smb2_readv_callback(struct mid_q_entry *mid)
task_io_account_read(rdata->got_bytes); task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes); cifs_stats_bytes_read(tcon, rdata->got_bytes);
break; break;
case MID_RESPONSE_MALFORMED:
credits_received = le16_to_cpu(shdr->CreditRequest);
/* fall through */
default: default:
if (rdata->result != -ENODATA) if (rdata->result != -ENODATA)
rdata->result = -EIO; rdata->result = -EIO;
...@@ -3399,7 +3411,7 @@ smb2_writev_callback(struct mid_q_entry *mid) ...@@ -3399,7 +3411,7 @@ smb2_writev_callback(struct mid_q_entry *mid)
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
unsigned int written; unsigned int written;
struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
unsigned int credits_received = 1; unsigned int credits_received = 0;
switch (mid->mid_state) { switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED: case MID_RESPONSE_RECEIVED:
...@@ -3427,6 +3439,9 @@ smb2_writev_callback(struct mid_q_entry *mid) ...@@ -3427,6 +3439,9 @@ smb2_writev_callback(struct mid_q_entry *mid)
case MID_RETRY_NEEDED: case MID_RETRY_NEEDED:
wdata->result = -EAGAIN; wdata->result = -EAGAIN;
break; break;
case MID_RESPONSE_MALFORMED:
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
/* fall through */
default: default:
wdata->result = -EIO; wdata->result = -EIO;
break; break;
......
...@@ -3,16 +3,6 @@ ...@@ -3,16 +3,6 @@
* Copyright (C) 2018, Microsoft Corporation. * Copyright (C) 2018, Microsoft Corporation.
* *
* Author(s): Steve French <stfrench@microsoft.com> * Author(s): Steve French <stfrench@microsoft.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/ */
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include "trace.h" #include "trace.h"
...@@ -3,16 +3,6 @@ ...@@ -3,16 +3,6 @@
* Copyright (C) 2018, Microsoft Corporation. * Copyright (C) 2018, Microsoft Corporation.
* *
* Author(s): Steve French <stfrench@microsoft.com> * Author(s): Steve French <stfrench@microsoft.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/ */
#undef TRACE_SYSTEM #undef TRACE_SYSTEM
#define TRACE_SYSTEM cifs #define TRACE_SYSTEM cifs
......
...@@ -786,17 +786,8 @@ static void ...@@ -786,17 +786,8 @@ static void
cifs_compound_callback(struct mid_q_entry *mid) cifs_compound_callback(struct mid_q_entry *mid)
{ {
struct TCP_Server_Info *server = mid->server; struct TCP_Server_Info *server = mid->server;
unsigned int optype = mid->optype;
unsigned int credits_received = 0;
if (mid->mid_state == MID_RESPONSE_RECEIVED) { add_credits(server, server->ops->get_credits(mid), mid->optype);
if (mid->resp_buf)
credits_received = server->ops->get_credits(mid);
else
cifs_dbg(FYI, "Bad state for cancelled MID\n");
}
add_credits(server, credits_received, optype);
} }
static void static void
......
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