#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 *);
 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
        .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);
 
        switch (call->operation_ID) {
        case CBCallBack:
        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;
        }
 
        return afs_queue_call_work(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 sockaddr_rxrpc srx;
+       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);
+
+       /* 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);
+}
 
--- /dev/null
+/* YFS protocol bits
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define YFS_FS_SERVICE 2500
+#define YFS_CM_SERVICE 2501
+
+#define YFSCBMAX 1024
+
+enum YFS_CM_Operations {
+       YFSCBProbe              = 206,  /* probe client */
+       YFSCBGetLock            = 207,  /* get contents of CM lock table */
+       YFSCBXStatsVersion      = 209,  /* get version of extended statistics */
+       YFSCBGetXStats          = 210,  /* get contents of extended statistics data */
+       YFSCBInitCallBackState3 = 213,  /* initialise callback state, version 3 */
+       YFSCBProbeUuid          = 214,  /* check the client hasn't rebooted */
+       YFSCBGetServerPrefs     = 215,
+       YFSCBGetCellServDV      = 216,
+       YFSCBGetLocalCell       = 217,
+       YFSCBGetCacheConfig     = 218,
+       YFSCBGetCellByNum       = 65537,
+       YFSCBTellMeAboutYourself = 65538, /* get client capabilities */
+       YFSCBCallBack           = 64204,
+};
+
+struct yfs_xdr_u64 {
+       __be32                  msw;
+       __be32                  lsw;
+} __packed;
+
+static inline u64 xdr_to_u64(const struct yfs_xdr_u64 x)
+{
+       return ((u64)ntohl(x.msw) << 32) | ntohl(x.lsw);
+}
+
+static inline struct yfs_xdr_u64 u64_to_xdr(const u64 x)
+{
+       return (struct yfs_xdr_u64){ .msw = htonl(x >> 32), .lsw = htonl(x) };
+}
+
+struct yfs_xdr_vnode {
+       struct yfs_xdr_u64      lo;
+       __be32                  hi;
+       __be32                  unique;
+} __packed;
+
+struct yfs_xdr_YFSFid {
+       struct yfs_xdr_u64      volume;
+       struct yfs_xdr_vnode    vnode;
+} __packed;
 
 #include <net/af_rxrpc.h>
 #include "internal.h"
 #include "afs_cm.h"
+#include "protocol_yfs.h"
 
 struct workqueue_struct *afs_async_calls;
 
        struct sockaddr_rxrpc srx;
        struct socket *socket;
        unsigned int min_level;
+       u16 service_upgrade[2];
        int ret;
 
        _enter("");
        if (ret < 0)
                goto error_2;
 
+       srx.srx_service = YFS_CM_SERVICE;
+       ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
+       if (ret < 0)
+               goto error_2;
+
+       service_upgrade[0] = CM_SERVICE;
+       service_upgrade[1] = YFS_CM_SERVICE;
+       ret = kernel_setsockopt(socket, SOL_RXRPC, RXRPC_UPGRADEABLE_SERVICE,
+                               (void *)service_upgrade, sizeof(service_upgrade));
+       if (ret < 0)
+               goto error_2;
+
+
        rxrpc_kernel_new_call_notification(socket, afs_rx_new_call,
                                           afs_rx_discard_new_call);