#include "cifsglob.h"
 #include "smb2pdu.h"
 #include "smb2proto.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+static int
+change_conf(struct TCP_Server_Info *server)
+{
+       server->credits += server->echo_credits + server->oplock_credits;
+       server->oplock_credits = server->echo_credits = 0;
+       switch (server->credits) {
+       case 0:
+               return -1;
+       case 1:
+               server->echoes = false;
+               server->oplocks = false;
+               cERROR(1, "disabling echoes and oplocks");
+               break;
+       case 2:
+               server->echoes = true;
+               server->oplocks = false;
+               server->echo_credits = 1;
+               cFYI(1, "disabling oplocks");
+               break;
+       default:
+               server->echoes = true;
+               server->oplocks = true;
+               server->echo_credits = 1;
+               server->oplock_credits = 1;
+       }
+       server->credits -= server->echo_credits + server->oplock_credits;
+       return 0;
+}
+
+static void
+smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
+                const int optype)
+{
+       int *val, rc = 0;
+       spin_lock(&server->req_lock);
+       val = server->ops->get_credits_field(server, optype);
+       *val += add;
+       server->in_flight--;
+       if (server->in_flight == 0)
+               rc = change_conf(server);
+       spin_unlock(&server->req_lock);
+       wake_up(&server->request_q);
+       if (rc)
+               cifs_reconnect(server);
+}
+
+static void
+smb2_set_credits(struct TCP_Server_Info *server, const int val)
+{
+       spin_lock(&server->req_lock);
+       server->credits = val;
+       spin_unlock(&server->req_lock);
+}
+
+static int *
+smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
+{
+       switch (optype) {
+       case CIFS_ECHO_OP:
+               return &server->echo_credits;
+       case CIFS_OBREAK_OP:
+               return &server->oplock_credits;
+       default:
+               return &server->credits;
+       }
+}
+
+static unsigned int
+smb2_get_credits(struct mid_q_entry *mid)
+{
+       return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
+}
 
 static __u64
 smb2_get_next_mid(struct TCP_Server_Info *server)
 struct smb_version_operations smb21_operations = {
        .setup_request = smb2_setup_request,
        .check_receive = smb2_check_receive,
+       .add_credits = smb2_add_credits,
+       .set_credits = smb2_set_credits,
+       .get_credits_field = smb2_get_credits_field,
+       .get_credits = smb2_get_credits,
        .get_next_mid = smb2_get_next_mid,
 };