Merge tag 'powerpc-4.21-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[sfrench/cifs-2.6.git] / fs / afs / cmservice.c
index 9e51d6fe7e8f975f34f877217a28a8e99bcfa5e4..8ee5972893ed5a75583bfb2821a42636403ee086 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/ip.h>
 #include "internal.h"
 #include "afs_cm.h"
+#include "protocol_yfs.h"
 
 static int afs_deliver_cb_init_call_back_state(struct afs_call *);
 static int afs_deliver_cb_init_call_back_state3(struct afs_call *);
@@ -30,6 +31,8 @@ static void SRXAFSCB_Probe(struct work_struct *);
 static void SRXAFSCB_ProbeUuid(struct work_struct *);
 static void SRXAFSCB_TellMeAboutYourself(struct work_struct *);
 
+static int afs_deliver_yfs_cb_callback(struct afs_call *);
+
 #define CM_NAME(name) \
        const char afs_SRXCB##name##_name[] __tracepoint_string =       \
                "CB." #name
@@ -100,13 +103,26 @@ static const struct afs_call_type afs_SRXCBTellMeAboutYourself = {
        .work           = SRXAFSCB_TellMeAboutYourself,
 };
 
+/*
+ * YFS CB.CallBack operation type
+ */
+static CM_NAME(YFS_CallBack);
+static const struct afs_call_type afs_SRXYFSCB_CallBack = {
+       .name           = afs_SRXCBYFS_CallBack_name,
+       .deliver        = afs_deliver_yfs_cb_callback,
+       .destructor     = afs_cm_destructor,
+       .work           = SRXAFSCB_CallBack,
+};
+
 /*
  * route an incoming cache manager call
  * - return T if supported, F if not
  */
 bool afs_cm_incoming_call(struct afs_call *call)
 {
-       _enter("{CB.OP %u}", call->operation_ID);
+       _enter("{%u, CB.OP %u}", call->service_id, call->operation_ID);
+
+       call->epoch = rxrpc_kernel_get_epoch(call->net->socket, call->rxcall);
 
        switch (call->operation_ID) {
        case CBCallBack:
@@ -127,11 +143,101 @@ bool afs_cm_incoming_call(struct afs_call *call)
        case CBTellMeAboutYourself:
                call->type = &afs_SRXCBTellMeAboutYourself;
                return true;
+       case YFSCBCallBack:
+               if (call->service_id != YFS_CM_SERVICE)
+                       return false;
+               call->type = &afs_SRXYFSCB_CallBack;
+               return true;
        default:
                return false;
        }
 }
 
+/*
+ * Record a probe to the cache manager from a server.
+ */
+static int afs_record_cm_probe(struct afs_call *call, struct afs_server *server)
+{
+       _enter("");
+
+       if (test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags) &&
+           !test_bit(AFS_SERVER_FL_PROBING, &server->flags)) {
+               if (server->cm_epoch == call->epoch)
+                       return 0;
+
+               if (!server->probe.said_rebooted) {
+                       pr_notice("kAFS: FS rebooted %pU\n", &server->uuid);
+                       server->probe.said_rebooted = true;
+               }
+       }
+
+       spin_lock(&server->probe_lock);
+
+       if (!test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags)) {
+               server->cm_epoch = call->epoch;
+               server->probe.cm_epoch = call->epoch;
+               goto out;
+       }
+
+       if (server->probe.cm_probed &&
+           call->epoch != server->probe.cm_epoch &&
+           !server->probe.said_inconsistent) {
+               pr_notice("kAFS: FS endpoints inconsistent %pU\n",
+                         &server->uuid);
+               server->probe.said_inconsistent = true;
+       }
+
+       if (!server->probe.cm_probed || call->epoch == server->cm_epoch)
+               server->probe.cm_epoch = server->cm_epoch;
+
+out:
+       server->probe.cm_probed = true;
+       spin_unlock(&server->probe_lock);
+       return 0;
+}
+
+/*
+ * Find the server record by peer address and record a probe to the cache
+ * manager from a server.
+ */
+static int afs_find_cm_server_by_peer(struct afs_call *call)
+{
+       struct sockaddr_rxrpc srx;
+       struct afs_server *server;
+
+       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
+
+       server = afs_find_server(call->net, &srx);
+       if (!server) {
+               trace_afs_cm_no_server(call, &srx);
+               return 0;
+       }
+
+       call->cm_server = server;
+       return afs_record_cm_probe(call, server);
+}
+
+/*
+ * Find the server record by server UUID and record a probe to the cache
+ * manager from a server.
+ */
+static int afs_find_cm_server_by_uuid(struct afs_call *call,
+                                     struct afs_uuid *uuid)
+{
+       struct afs_server *server;
+
+       rcu_read_lock();
+       server = afs_find_server_by_uuid(call->net, call->request);
+       rcu_read_unlock();
+       if (!server) {
+               trace_afs_cm_no_server_u(call, call->request);
+               return 0;
+       }
+
+       call->cm_server = server;
+       return afs_record_cm_probe(call, server);
+}
+
 /*
  * Clean up a cache manager call.
  */
@@ -168,7 +274,6 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
 static int afs_deliver_cb_callback(struct afs_call *call)
 {
        struct afs_callback_break *cb;
-       struct sockaddr_rxrpc srx;
        __be32 *bp;
        int ret, loop;
 
@@ -176,32 +281,32 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 
        switch (call->unmarshall) {
        case 0:
-               call->offset = 0;
+               afs_extract_to_tmp(call);
                call->unmarshall++;
 
                /* extract the FID array and its count in two steps */
        case 1:
                _debug("extract FID count");
-               ret = afs_extract_data(call, &call->tmp, 4, true);
+               ret = afs_extract_data(call, true);
                if (ret < 0)
                        return ret;
 
                call->count = ntohl(call->tmp);
                _debug("FID count: %u", call->count);
                if (call->count > AFSCBMAX)
-                       return afs_protocol_error(call, -EBADMSG);
+                       return afs_protocol_error(call, -EBADMSG,
+                                                 afs_eproto_cb_fid_count);
 
                call->buffer = kmalloc(array3_size(call->count, 3, 4),
                                       GFP_KERNEL);
                if (!call->buffer)
                        return -ENOMEM;
-               call->offset = 0;
+               afs_extract_to_buf(call, call->count * 3 * 4);
                call->unmarshall++;
 
        case 2:
                _debug("extract FID array");
-               ret = afs_extract_data(call, call->buffer,
-                                      call->count * 3 * 4, true);
+               ret = afs_extract_data(call, true);
                if (ret < 0)
                        return ret;
 
@@ -218,59 +323,46 @@ static int afs_deliver_cb_callback(struct afs_call *call)
                        cb->fid.vid     = ntohl(*bp++);
                        cb->fid.vnode   = ntohl(*bp++);
                        cb->fid.unique  = ntohl(*bp++);
-                       cb->cb.type     = AFSCM_CB_UNTYPED;
                }
 
-               call->offset = 0;
+               afs_extract_to_tmp(call);
                call->unmarshall++;
 
                /* extract the callback array and its count in two steps */
        case 3:
                _debug("extract CB count");
-               ret = afs_extract_data(call, &call->tmp, 4, true);
+               ret = afs_extract_data(call, true);
                if (ret < 0)
                        return ret;
 
                call->count2 = ntohl(call->tmp);
                _debug("CB count: %u", call->count2);
                if (call->count2 != call->count && call->count2 != 0)
-                       return afs_protocol_error(call, -EBADMSG);
-               call->offset = 0;
+                       return afs_protocol_error(call, -EBADMSG,
+                                                 afs_eproto_cb_count);
+               call->_iter = &call->iter;
+               iov_iter_discard(&call->iter, READ, call->count2 * 3 * 4);
                call->unmarshall++;
 
        case 4:
-               _debug("extract CB array");
-               ret = afs_extract_data(call, call->buffer,
-                                      call->count2 * 3 * 4, false);
+               _debug("extract discard %zu/%u",
+                      iov_iter_count(&call->iter), call->count2 * 3 * 4);
+
+               ret = afs_extract_data(call, false);
                if (ret < 0)
                        return ret;
 
-               _debug("unmarshall CB array");
-               cb = call->request;
-               bp = call->buffer;
-               for (loop = call->count2; loop > 0; loop--, cb++) {
-                       cb->cb.version  = ntohl(*bp++);
-                       cb->cb.expiry   = ntohl(*bp++);
-                       cb->cb.type     = ntohl(*bp++);
-               }
-
-               call->offset = 0;
                call->unmarshall++;
        case 5:
                break;
        }
 
        if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
-               return -EIO;
+               return afs_io_error(call, afs_io_error_cm_reply);
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
-       call->cm_server = afs_find_server(call->net, &srx);
-       if (!call->cm_server)
-               trace_afs_cm_no_server(call, &srx);
-
-       return afs_queue_call_work(call);
+       return afs_find_cm_server_by_peer(call);
 }
 
 /*
@@ -294,24 +386,18 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
  */
 static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
 {
-       struct sockaddr_rxrpc srx;
        int ret;
 
        _enter("");
 
-       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
-
-       ret = afs_extract_data(call, NULL, 0, false);
+       afs_extract_discard(call, 0);
+       ret = afs_extract_data(call, false);
        if (ret < 0)
                return ret;
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       call->cm_server = afs_find_server(call->net, &srx);
-       if (!call->cm_server)
-               trace_afs_cm_no_server(call, &srx);
-
-       return afs_queue_call_work(call);
+       return afs_find_cm_server_by_peer(call);
 }
 
 /*
@@ -330,16 +416,15 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
 
        switch (call->unmarshall) {
        case 0:
-               call->offset = 0;
                call->buffer = kmalloc_array(11, sizeof(__be32), GFP_KERNEL);
                if (!call->buffer)
                        return -ENOMEM;
+               afs_extract_to_buf(call, 11 * sizeof(__be32));
                call->unmarshall++;
 
        case 1:
                _debug("extract UUID");
-               ret = afs_extract_data(call, call->buffer,
-                                      11 * sizeof(__be32), false);
+               ret = afs_extract_data(call, false);
                switch (ret) {
                case 0:         break;
                case -EAGAIN:   return 0;
@@ -362,7 +447,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
                for (loop = 0; loop < 6; loop++)
                        r->node[loop] = ntohl(b[loop + 5]);
 
-               call->offset = 0;
                call->unmarshall++;
 
        case 2:
@@ -370,17 +454,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
        }
 
        if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
-               return -EIO;
+               return afs_io_error(call, afs_io_error_cm_reply);
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       rcu_read_lock();
-       call->cm_server = afs_find_server_by_uuid(call->net, call->request);
-       rcu_read_unlock();
-       if (!call->cm_server)
-               trace_afs_cm_no_server_u(call, call->request);
-
-       return afs_queue_call_work(call);
+       return afs_find_cm_server_by_uuid(call, call->request);
 }
 
 /*
@@ -405,14 +483,14 @@ static int afs_deliver_cb_probe(struct afs_call *call)
 
        _enter("");
 
-       ret = afs_extract_data(call, NULL, 0, false);
+       afs_extract_discard(call, 0);
+       ret = afs_extract_data(call, false);
        if (ret < 0)
                return ret;
 
        if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
-               return -EIO;
-
-       return afs_queue_call_work(call);
+               return afs_io_error(call, afs_io_error_cm_reply);
+       return afs_find_cm_server_by_peer(call);
 }
 
 /*
@@ -453,16 +531,15 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
 
        switch (call->unmarshall) {
        case 0:
-               call->offset = 0;
                call->buffer = kmalloc_array(11, sizeof(__be32), GFP_KERNEL);
                if (!call->buffer)
                        return -ENOMEM;
+               afs_extract_to_buf(call, 11 * sizeof(__be32));
                call->unmarshall++;
 
        case 1:
                _debug("extract UUID");
-               ret = afs_extract_data(call, call->buffer,
-                                      11 * sizeof(__be32), false);
+               ret = afs_extract_data(call, false);
                switch (ret) {
                case 0:         break;
                case -EAGAIN:   return 0;
@@ -485,7 +562,6 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
                for (loop = 0; loop < 6; loop++)
                        r->node[loop] = ntohl(b[loop + 5]);
 
-               call->offset = 0;
                call->unmarshall++;
 
        case 2:
@@ -493,9 +569,8 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
        }
 
        if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
-               return -EIO;
-
-       return afs_queue_call_work(call);
+               return afs_io_error(call, afs_io_error_cm_reply);
+       return afs_find_cm_server_by_uuid(call, call->request);
 }
 
 /*
@@ -570,12 +645,88 @@ static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *call)
 
        _enter("");
 
-       ret = afs_extract_data(call, NULL, 0, false);
+       afs_extract_discard(call, 0);
+       ret = afs_extract_data(call, false);
        if (ret < 0)
                return ret;
 
        if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
-               return -EIO;
+               return afs_io_error(call, afs_io_error_cm_reply);
+       return afs_find_cm_server_by_peer(call);
+}
+
+/*
+ * deliver request data to a YFS CB.CallBack call
+ */
+static int afs_deliver_yfs_cb_callback(struct afs_call *call)
+{
+       struct afs_callback_break *cb;
+       struct yfs_xdr_YFSFid *bp;
+       size_t size;
+       int ret, loop;
+
+       _enter("{%u}", call->unmarshall);
+
+       switch (call->unmarshall) {
+       case 0:
+               afs_extract_to_tmp(call);
+               call->unmarshall++;
+
+               /* extract the FID array and its count in two steps */
+       case 1:
+               _debug("extract FID count");
+               ret = afs_extract_data(call, true);
+               if (ret < 0)
+                       return ret;
+
+               call->count = ntohl(call->tmp);
+               _debug("FID count: %u", call->count);
+               if (call->count > YFSCBMAX)
+                       return afs_protocol_error(call, -EBADMSG,
+                                                 afs_eproto_cb_fid_count);
+
+               size = array_size(call->count, sizeof(struct yfs_xdr_YFSFid));
+               call->buffer = kmalloc(size, GFP_KERNEL);
+               if (!call->buffer)
+                       return -ENOMEM;
+               afs_extract_to_buf(call, size);
+               call->unmarshall++;
+
+       case 2:
+               _debug("extract FID array");
+               ret = afs_extract_data(call, false);
+               if (ret < 0)
+                       return ret;
+
+               _debug("unmarshall FID array");
+               call->request = kcalloc(call->count,
+                                       sizeof(struct afs_callback_break),
+                                       GFP_KERNEL);
+               if (!call->request)
+                       return -ENOMEM;
+
+               cb = call->request;
+               bp = call->buffer;
+               for (loop = call->count; loop > 0; loop--, cb++) {
+                       cb->fid.vid     = xdr_to_u64(bp->volume);
+                       cb->fid.vnode   = xdr_to_u64(bp->vnode.lo);
+                       cb->fid.vnode_hi = ntohl(bp->vnode.hi);
+                       cb->fid.unique  = ntohl(bp->vnode.unique);
+                       bp++;
+               }
+
+               afs_extract_to_tmp(call);
+               call->unmarshall++;
+
+       case 3:
+               break;
+       }
+
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return afs_io_error(call, afs_io_error_cm_reply);
 
-       return afs_queue_call_work(call);
+       /* We'll need the file server record as that tells us which set of
+        * vnodes to operate upon.
+        */
+       return afs_find_cm_server_by_peer(call);
 }