Merge branches 'imx/pata' and 'imx/sata' into next/driver
[pandora-kernel.git] / fs / nfs / callback_xdr.c
index 00ecf62..918ad64 100644 (file)
@@ -25,6 +25,7 @@
 
 #if defined(CONFIG_NFS_V4_1)
 #define CB_OP_LAYOUTRECALL_RES_MAXSZ   (CB_OP_HDR_RES_MAXSZ)
+#define CB_OP_DEVICENOTIFY_RES_MAXSZ   (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_SEQUENCE_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ + \
                                        4 + 1 + 3)
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
@@ -284,6 +285,93 @@ out:
        return status;
 }
 
+static
+__be32 decode_devicenotify_args(struct svc_rqst *rqstp,
+                               struct xdr_stream *xdr,
+                               struct cb_devicenotifyargs *args)
+{
+       __be32 *p;
+       __be32 status = 0;
+       u32 tmp;
+       int n, i;
+       args->ndevs = 0;
+
+       /* Num of device notifications */
+       p = read_buf(xdr, sizeof(uint32_t));
+       if (unlikely(p == NULL)) {
+               status = htonl(NFS4ERR_BADXDR);
+               goto out;
+       }
+       n = ntohl(*p++);
+       if (n <= 0)
+               goto out;
+
+       args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL);
+       if (!args->devs) {
+               status = htonl(NFS4ERR_DELAY);
+               goto out;
+       }
+
+       /* Decode each dev notification */
+       for (i = 0; i < n; i++) {
+               struct cb_devicenotifyitem *dev = &args->devs[i];
+
+               p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE);
+               if (unlikely(p == NULL)) {
+                       status = htonl(NFS4ERR_BADXDR);
+                       goto err;
+               }
+
+               tmp = ntohl(*p++);      /* bitmap size */
+               if (tmp != 1) {
+                       status = htonl(NFS4ERR_INVAL);
+                       goto err;
+               }
+               dev->cbd_notify_type = ntohl(*p++);
+               if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE &&
+                   dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) {
+                       status = htonl(NFS4ERR_INVAL);
+                       goto err;
+               }
+
+               tmp = ntohl(*p++);      /* opaque size */
+               if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) &&
+                    (tmp != NFS4_DEVICEID4_SIZE + 8)) ||
+                   ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) &&
+                    (tmp != NFS4_DEVICEID4_SIZE + 4))) {
+                       status = htonl(NFS4ERR_INVAL);
+                       goto err;
+               }
+               dev->cbd_layout_type = ntohl(*p++);
+               memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE);
+               p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
+
+               if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) {
+                       p = read_buf(xdr, sizeof(uint32_t));
+                       if (unlikely(p == NULL)) {
+                               status = htonl(NFS4ERR_BADXDR);
+                               goto err;
+                       }
+                       dev->cbd_immediate = ntohl(*p++);
+               } else {
+                       dev->cbd_immediate = 0;
+               }
+
+               args->ndevs++;
+
+               dprintk("%s: type %d layout 0x%x immediate %d\n",
+                       __func__, dev->cbd_notify_type, dev->cbd_layout_type,
+                       dev->cbd_immediate);
+       }
+out:
+       dprintk("%s: status %d ndevs %d\n",
+               __func__, ntohl(status), args->ndevs);
+       return status;
+err:
+       kfree(args->devs);
+       goto out;
+}
+
 static __be32 decode_sessionid(struct xdr_stream *xdr,
                                 struct nfs4_sessionid *sid)
 {
@@ -639,10 +727,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_RECALL_ANY:
        case OP_CB_RECALL_SLOT:
        case OP_CB_LAYOUTRECALL:
+       case OP_CB_NOTIFY_DEVICEID:
                *op = &callback_ops[op_nr];
                break;
 
-       case OP_CB_NOTIFY_DEVICEID:
        case OP_CB_NOTIFY:
        case OP_CB_PUSH_DELEG:
        case OP_CB_RECALLABLE_OBJ_AVAIL:
@@ -666,26 +754,15 @@ static void nfs4_callback_free_slot(struct nfs4_session *session)
         * Let the state manager know callback processing done.
         * A single slot, so highest used slotid is either 0 or -1
         */
-       tbl->highest_used_slotid--;
+       tbl->highest_used_slotid = -1;
        nfs4_check_drain_bc_complete(session);
        spin_unlock(&tbl->slot_tbl_lock);
 }
 
-static void nfs4_cb_free_slot(struct nfs_client *clp)
-{
-       if (clp && clp->cl_session)
-               nfs4_callback_free_slot(clp->cl_session);
-}
-
-/* A single slot, so highest used slotid is either 0 or -1 */
-void nfs4_cb_take_slot(struct nfs_client *clp)
+static void nfs4_cb_free_slot(struct cb_process_state *cps)
 {
-       struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table;
-
-       spin_lock(&tbl->slot_tbl_lock);
-       tbl->highest_used_slotid++;
-       BUG_ON(tbl->highest_used_slotid != 0);
-       spin_unlock(&tbl->slot_tbl_lock);
+       if (cps->slotid != -1)
+               nfs4_callback_free_slot(cps->clp->cl_session);
 }
 
 #else /* CONFIG_NFS_V4_1 */
@@ -696,7 +773,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 }
 
-static void nfs4_cb_free_slot(struct nfs_client *clp)
+static void nfs4_cb_free_slot(struct cb_process_state *cps)
 {
 }
 #endif /* CONFIG_NFS_V4_1 */
@@ -778,6 +855,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        struct cb_process_state cps = {
                .drc_status = 0,
                .clp = NULL,
+               .slotid = -1,
        };
        unsigned int nops = 0;
 
@@ -818,7 +896,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 
        *hdr_res.status = status;
        *hdr_res.nops = htonl(nops);
-       nfs4_cb_free_slot(cps.clp);
+       nfs4_cb_free_slot(&cps);
        nfs_put_client(cps.clp);
        dprintk("%s: done, status = %u\n", __func__, ntohl(status));
        return rpc_success;
@@ -849,6 +927,12 @@ static struct callback_op callback_ops[] = {
                        (callback_decode_arg_t)decode_layoutrecall_args,
                .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
        },
+       [OP_CB_NOTIFY_DEVICEID] = {
+               .process_op = (callback_process_op_t)nfs4_callback_devicenotify,
+               .decode_args =
+                       (callback_decode_arg_t)decode_devicenotify_args,
+               .res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ,
+       },
        [OP_CB_SEQUENCE] = {
                .process_op = (callback_process_op_t)nfs4_callback_sequence,
                .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,