Exemple #1
0
/**
 * nx_hcall_sync - make an H_COP_OP hcall for the passed in op structure
 *
 * @nx_ctx: the crypto context handle
 * @op: PFO operation struct to pass in
 * @may_sleep: flag indicating the request can sleep
 *
 * Make the hcall, retrying while the hardware is busy. If we cannot yield
 * the thread, limit the number of retries to 10 here.
 */
int nx_hcall_sync(struct nx_crypto_ctx *nx_ctx,
		  struct vio_pfo_op    *op,
		  u32                   may_sleep)
{
	int rc, retries = 10;
	struct vio_dev *viodev = nx_driver.viodev;

	atomic_inc(&(nx_ctx->stats->sync_ops));

	do {
		rc = vio_h_cop_sync(viodev, op);
	} while ((rc == -EBUSY && !may_sleep && retries--) ||
	         (rc == -EBUSY && may_sleep && cond_resched()));

	if (rc) {
		dev_dbg(&viodev->dev, "vio_h_cop_sync failed: rc: %d "
			"hcall rc: %ld\n", rc, op->hcall_err);
		atomic_inc(&(nx_ctx->stats->errors));
		atomic_set(&(nx_ctx->stats->last_error), op->hcall_err);
		atomic_set(&(nx_ctx->stats->last_error_pid), current->pid);
	}

	return rc;
}
Exemple #2
0
/**
 * nx842_pseries_compress - Compress data using the 842 algorithm
 *
 * Compression provide by the NX842 coprocessor on IBM Power systems.
 * The input buffer is compressed and the result is stored in the
 * provided output buffer.
 *
 * Upon return from this function @outlen contains the length of the
 * compressed data.  If there is an error then @outlen will be 0 and an
 * error will be specified by the return code from this function.
 *
 * @in: Pointer to input buffer
 * @inlen: Length of input buffer
 * @out: Pointer to output buffer
 * @outlen: Length of output buffer
 * @wrkmem: ptr to buffer for working memory, size determined by
 *          nx842_pseries_driver.workmem_size
 *
 * Returns:
 *   0		Success, output of length @outlen stored in the buffer at @out
 *   -ENOMEM	Unable to allocate internal buffers
 *   -ENOSPC	Output buffer is to small
 *   -EIO	Internal error
 *   -ENODEV	Hardware unavailable
 */
static int nx842_pseries_compress(const unsigned char *in, unsigned int inlen,
				  unsigned char *out, unsigned int *outlen,
				  void *wmem)
{
	struct nx842_devdata *local_devdata;
	struct device *dev = NULL;
	struct nx842_workmem *workmem;
	struct nx842_scatterlist slin, slout;
	struct nx_csbcpb *csbcpb;
	int ret = 0, max_sync_size;
	unsigned long inbuf, outbuf;
	struct vio_pfo_op op = {
		.done = NULL,
		.handle = 0,
		.timeout = 0,
	};
	unsigned long start = get_tb();

	inbuf = (unsigned long)in;
	if (check_constraints(inbuf, &inlen, true))
		return -EINVAL;

	outbuf = (unsigned long)out;
	if (check_constraints(outbuf, outlen, false))
		return -EINVAL;

	rcu_read_lock();
	local_devdata = rcu_dereference(devdata);
	if (!local_devdata || !local_devdata->dev) {
		rcu_read_unlock();
		return -ENODEV;
	}
	max_sync_size = local_devdata->max_sync_size;
	dev = local_devdata->dev;

	/* Init scatterlist */
	workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);
	slin.entries = (struct nx842_slentry *)workmem->slin;
	slout.entries = (struct nx842_slentry *)workmem->slout;

	/* Init operation */
	op.flags = NX842_OP_COMPRESS;
	csbcpb = &workmem->csbcpb;
	memset(csbcpb, 0, sizeof(*csbcpb));
	op.csbcpb = nx842_get_pa(csbcpb);

	if ((inbuf & NX842_HW_PAGE_MASK) ==
	    ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
		/* Create direct DDE */
		op.in = nx842_get_pa((void *)inbuf);
		op.inlen = inlen;
	} else {
		/* Create indirect DDE (scatterlist) */
		nx842_build_scatterlist(inbuf, inlen, &slin);
		op.in = nx842_get_pa(slin.entries);
		op.inlen = -nx842_get_scatterlist_size(&slin);
	}

	if ((outbuf & NX842_HW_PAGE_MASK) ==
	    ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
		/* Create direct DDE */
		op.out = nx842_get_pa((void *)outbuf);
		op.outlen = *outlen;
	} else {
		/* Create indirect DDE (scatterlist) */
		nx842_build_scatterlist(outbuf, *outlen, &slout);
		op.out = nx842_get_pa(slout.entries);
		op.outlen = -nx842_get_scatterlist_size(&slout);
	}

	dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
		__func__, (unsigned long)op.in, (long)op.inlen,
		(unsigned long)op.out, (long)op.outlen);

	/* Send request to pHyp */
	ret = vio_h_cop_sync(local_devdata->vdev, &op);

	/* Check for pHyp error */
	if (ret) {
		dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
			__func__, ret, op.hcall_err);
		ret = -EIO;
		goto unlock;
	}

	/* Check for hardware error */
	ret = nx842_validate_result(dev, &csbcpb->csb);
	if (ret)
		goto unlock;

	*outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);
	dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen);

unlock:
	if (ret)
		nx842_inc_comp_failed(local_devdata);
	else {
		nx842_inc_comp_complete(local_devdata);
		ibm_nx842_incr_hist(local_devdata->counters->comp_times,
			(get_tb() - start) / tb_ticks_per_usec);
	}
	rcu_read_unlock();
	return ret;
}

/**
 * nx842_pseries_decompress - Decompress data using the 842 algorithm
 *
 * Decompression provide by the NX842 coprocessor on IBM Power systems.
 * The input buffer is decompressed and the result is stored in the
 * provided output buffer.  The size allocated to the output buffer is
 * provided by the caller of this function in @outlen.  Upon return from
 * this function @outlen contains the length of the decompressed data.
 * If there is an error then @outlen will be 0 and an error will be
 * specified by the return code from this function.
 *
 * @in: Pointer to input buffer
 * @inlen: Length of input buffer
 * @out: Pointer to output buffer
 * @outlen: Length of output buffer
 * @wrkmem: ptr to buffer for working memory, size determined by
 *          nx842_pseries_driver.workmem_size
 *
 * Returns:
 *   0		Success, output of length @outlen stored in the buffer at @out
 *   -ENODEV	Hardware decompression device is unavailable
 *   -ENOMEM	Unable to allocate internal buffers
 *   -ENOSPC	Output buffer is to small
 *   -EINVAL	Bad input data encountered when attempting decompress
 *   -EIO	Internal error
 */
static int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen,
				    unsigned char *out, unsigned int *outlen,
				    void *wmem)
{
	struct nx842_devdata *local_devdata;
	struct device *dev = NULL;
	struct nx842_workmem *workmem;
	struct nx842_scatterlist slin, slout;
	struct nx_csbcpb *csbcpb;
	int ret = 0, max_sync_size;
	unsigned long inbuf, outbuf;
	struct vio_pfo_op op = {
		.done = NULL,
		.handle = 0,
		.timeout = 0,
	};
	unsigned long start = get_tb();

	/* Ensure page alignment and size */
	inbuf = (unsigned long)in;
	if (check_constraints(inbuf, &inlen, true))
		return -EINVAL;

	outbuf = (unsigned long)out;
	if (check_constraints(outbuf, outlen, false))
		return -EINVAL;

	rcu_read_lock();
	local_devdata = rcu_dereference(devdata);
	if (!local_devdata || !local_devdata->dev) {
		rcu_read_unlock();
		return -ENODEV;
	}
	max_sync_size = local_devdata->max_sync_size;
	dev = local_devdata->dev;

	workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);

	/* Init scatterlist */
	slin.entries = (struct nx842_slentry *)workmem->slin;
	slout.entries = (struct nx842_slentry *)workmem->slout;

	/* Init operation */
	op.flags = NX842_OP_DECOMPRESS;
	csbcpb = &workmem->csbcpb;
	memset(csbcpb, 0, sizeof(*csbcpb));
	op.csbcpb = nx842_get_pa(csbcpb);

	if ((inbuf & NX842_HW_PAGE_MASK) ==
	    ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
		/* Create direct DDE */
		op.in = nx842_get_pa((void *)inbuf);
		op.inlen = inlen;
	} else {
		/* Create indirect DDE (scatterlist) */
		nx842_build_scatterlist(inbuf, inlen, &slin);
		op.in = nx842_get_pa(slin.entries);
		op.inlen = -nx842_get_scatterlist_size(&slin);
	}

	if ((outbuf & NX842_HW_PAGE_MASK) ==
	    ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
		/* Create direct DDE */
		op.out = nx842_get_pa((void *)outbuf);
		op.outlen = *outlen;
	} else {
		/* Create indirect DDE (scatterlist) */
		nx842_build_scatterlist(outbuf, *outlen, &slout);
		op.out = nx842_get_pa(slout.entries);
		op.outlen = -nx842_get_scatterlist_size(&slout);
	}

	dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
		__func__, (unsigned long)op.in, (long)op.inlen,
		(unsigned long)op.out, (long)op.outlen);

	/* Send request to pHyp */
	ret = vio_h_cop_sync(local_devdata->vdev, &op);

	/* Check for pHyp error */
	if (ret) {
		dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
			__func__, ret, op.hcall_err);
		goto unlock;
	}

	/* Check for hardware error */
	ret = nx842_validate_result(dev, &csbcpb->csb);
	if (ret)
		goto unlock;

	*outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);

unlock:
	if (ret)
		/* decompress fail */
		nx842_inc_decomp_failed(local_devdata);
	else {
		nx842_inc_decomp_complete(local_devdata);
		ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
			(get_tb() - start) / tb_ticks_per_usec);
	}

	rcu_read_unlock();
	return ret;
}

/**
 * nx842_OF_set_defaults -- Set default (disabled) values for devdata
 *
 * @devdata - struct nx842_devdata to update
 *
 * Returns:
 *  0 on success
 *  -ENOENT if @devdata ptr is NULL
 */
static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
{
	if (devdata) {
		devdata->max_sync_size = 0;
		devdata->max_sync_sg = 0;
		devdata->max_sg_len = 0;
		return 0;
	} else
		return -ENOENT;
}

/**
 * nx842_OF_upd_status -- Check the device info from OF status prop
 *
 * The status property indicates if the accelerator is enabled.  If the
 * device is in the OF tree it indicates that the hardware is present.
 * The status field indicates if the device is enabled when the status
 * is 'okay'.  Otherwise the device driver will be disabled.
 *
 * @prop - struct property point containing the maxsyncop for the update
 *
 * Returns:
 *  0 - Device is available
 *  -ENODEV - Device is not available
 */
static int nx842_OF_upd_status(struct property *prop)
{
	const char *status = (const char *)prop->value;

	if (!strncmp(status, "okay", (size_t)prop->length))
		return 0;
	if (!strncmp(status, "disabled", (size_t)prop->length))
		return -ENODEV;
	dev_info(devdata->dev, "%s: unknown status '%s'\n", __func__, status);

	return -EINVAL;
}

/**
 * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
 *
 * Definition of the 'ibm,max-sg-len' OF property:
 *  This field indicates the maximum byte length of a scatter list
 *  for the platform facility. It is a single cell encoded as with encode-int.
 *
 * Example:
 *  # od -x ibm,max-sg-len
 *  0000000 0000 0ff0
 *
 *  In this example, the maximum byte length of a scatter list is
 *  0x0ff0 (4,080).
 *
 * @devdata - struct nx842_devdata to update
 * @prop - struct property point containing the maxsyncop for the update
 *
 * Returns:
 *  0 on success
 *  -EINVAL on failure
 */
static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
					struct property *prop) {
	int ret = 0;
	const unsigned int maxsglen = of_read_number(prop->value, 1);

	if (prop->length != sizeof(maxsglen)) {
		dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
		dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
				prop->length, sizeof(maxsglen));
		ret = -EINVAL;
	} else {
		devdata->max_sg_len = min_t(unsigned int,
					    maxsglen, NX842_HW_PAGE_SIZE);
	}

	return ret;
}

/**
 * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop
 *
 * Definition of the 'ibm,max-sync-cop' OF property:
 *  Two series of cells.  The first series of cells represents the maximums
 *  that can be synchronously compressed. The second series of cells
 *  represents the maximums that can be synchronously decompressed.
 *  1. The first cell in each series contains the count of the number of
 *     data length, scatter list elements pairs that follow – each being
 *     of the form
 *    a. One cell data byte length
 *    b. One cell total number of scatter list elements
 *
 * Example:
 *  # od -x ibm,max-sync-cop
 *  0000000 0000 0001 0000 1000 0000 01fe 0000 0001
 *  0000020 0000 1000 0000 01fe
 *
 *  In this example, compression supports 0x1000 (4,096) data byte length
 *  and 0x1fe (510) total scatter list elements.  Decompression supports
 *  0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list
 *  elements.
 *
 * @devdata - struct nx842_devdata to update
 * @prop - struct property point containing the maxsyncop for the update
 *
 * Returns:
 *  0 on success
 *  -EINVAL on failure
 */
static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata,
					struct property *prop) {
	int ret = 0;
	unsigned int comp_data_limit, decomp_data_limit;
	unsigned int comp_sg_limit, decomp_sg_limit;
	const struct maxsynccop_t {
		__be32 comp_elements;
		__be32 comp_data_limit;
		__be32 comp_sg_limit;
		__be32 decomp_elements;
		__be32 decomp_data_limit;
		__be32 decomp_sg_limit;
	} *maxsynccop;

	if (prop->length != sizeof(*maxsynccop)) {
		dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__);
		dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length,
				sizeof(*maxsynccop));
		ret = -EINVAL;
		goto out;
	}

	maxsynccop = (const struct maxsynccop_t *)prop->value;
	comp_data_limit = be32_to_cpu(maxsynccop->comp_data_limit);
	comp_sg_limit = be32_to_cpu(maxsynccop->comp_sg_limit);
	decomp_data_limit = be32_to_cpu(maxsynccop->decomp_data_limit);
	decomp_sg_limit = be32_to_cpu(maxsynccop->decomp_sg_limit);

	/* Use one limit rather than separate limits for compression and
	 * decompression. Set a maximum for this so as not to exceed the
	 * size that the header can support and round the value down to
	 * the hardware page size (4K) */
	devdata->max_sync_size = min(comp_data_limit, decomp_data_limit);

	devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size,
					65536);

	if (devdata->max_sync_size < 4096) {
		dev_err(devdata->dev, "%s: hardware max data size (%u) is "
				"less than the driver minimum, unable to use "
				"the hardware device\n",
				__func__, devdata->max_sync_size);
		ret = -EINVAL;
		goto out;
	}

	nx842_pseries_constraints.maximum = devdata->max_sync_size;

	devdata->max_sync_sg = min(comp_sg_limit, decomp_sg_limit);
	if (devdata->max_sync_sg < 1) {
		dev_err(devdata->dev, "%s: hardware max sg size (%u) is "
				"less than the driver minimum, unable to use "
				"the hardware device\n",
				__func__, devdata->max_sync_sg);
		ret = -EINVAL;
		goto out;
	}

out:
	return ret;
}
Exemple #3
0
/**
 * nx842_compress - Compress data using the 842 algorithm
 *
 * Compression provide by the NX842 coprocessor on IBM Power systems.
 * The input buffer is compressed and the result is stored in the
 * provided output buffer.
 *
 * Upon return from this function @outlen contains the length of the
 * compressed data.  If there is an error then @outlen will be 0 and an
 * error will be specified by the return code from this function.
 *
 * @in: Pointer to input buffer, must be page aligned
 * @inlen: Length of input buffer, must be PAGE_SIZE
 * @out: Pointer to output buffer
 * @outlen: Length of output buffer
 * @wrkmem: ptr to buffer for working memory, size determined by
 *          nx842_get_workmem_size()
 *
 * Returns:
 *   0		Success, output of length @outlen stored in the buffer at @out
 *   -ENOMEM	Unable to allocate internal buffers
 *   -ENOSPC	Output buffer is to small
 *   -EMSGSIZE	XXX Difficult to describe this limitation
 *   -EIO	Internal error
 *   -ENODEV	Hardware unavailable
 */
int nx842_compress(const unsigned char *in, unsigned int inlen,
		       unsigned char *out, unsigned int *outlen, void *wmem)
{
	struct nx842_header *hdr;
	struct nx842_devdata *local_devdata;
	struct device *dev = NULL;
	struct nx842_workmem *workmem;
	struct nx842_scatterlist slin, slout;
	struct nx_csbcpb *csbcpb;
	int ret = 0, max_sync_size, i, bytesleft, size, hdrsize;
	unsigned long inbuf, outbuf, padding;
	struct vio_pfo_op op = {
		.done = NULL,
		.handle = 0,
		.timeout = 0,
	};
	unsigned long start_time = get_tb();

	/*
	 * Make sure input buffer is 64k page aligned.  This is assumed since
	 * this driver is designed for page compression only (for now).  This
	 * is very nice since we can now use direct DDE(s) for the input and
	 * the alignment is guaranteed.
	*/
	inbuf = (unsigned long)in;
	if (!IS_ALIGNED(inbuf, PAGE_SIZE) || inlen != PAGE_SIZE)
		return -EINVAL;

	rcu_read_lock();
	local_devdata = rcu_dereference(devdata);
	if (!local_devdata || !local_devdata->dev) {
		rcu_read_unlock();
		return -ENODEV;
	}
	max_sync_size = local_devdata->max_sync_size;
	dev = local_devdata->dev;

	/* Create the header */
	hdr = (struct nx842_header *)out;
	hdr->blocks_nr = PAGE_SIZE / max_sync_size;
	hdrsize = nx842_header_size(hdr);
	outbuf = (unsigned long)out + hdrsize;
	bytesleft = *outlen - hdrsize;

	/* Init scatterlist */
	workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
		NX842_HW_PAGE_SIZE);
	slin.entries = (struct nx842_slentry *)workmem->slin;
	slout.entries = (struct nx842_slentry *)workmem->slout;

	/* Init operation */
	op.flags = NX842_OP_COMPRESS;
	csbcpb = &workmem->csbcpb;
	memset(csbcpb, 0, sizeof(*csbcpb));
	op.csbcpb = nx842_get_pa(csbcpb);
	op.out = nx842_get_pa(slout.entries);

	for (i = 0; i < hdr->blocks_nr; i++) {
		/*
		 * Aligning the output blocks to 128 bytes does waste space,
		 * but it prevents the need for bounce buffers and memory
		 * copies.  It also simplifies the code a lot.  In the worst
		 * case (64k page, 4k max_sync_size), you lose up to
		 * (128*16)/64k = ~3% the compression factor. For 64k
		 * max_sync_size, the loss would be at most 128/64k = ~0.2%.
		 */
		padding = ALIGN(outbuf, IO_BUFFER_ALIGN) - outbuf;
		outbuf += padding;
		bytesleft -= padding;
		if (i == 0)
			/* save offset into first block in header */
			hdr->offset = padding + hdrsize;

		if (bytesleft <= 0) {
			ret = -ENOSPC;
			goto unlock;
		}

		/*
		 * NOTE: If the default max_sync_size is changed from 4k
		 * to 64k, remove the "likely" case below, since a
		 * scatterlist will always be needed.
		 */
		if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
			/* Create direct DDE */
			op.in = nx842_get_pa((void *)inbuf);
			op.inlen = max_sync_size;

		} else {
			/* Create indirect DDE (scatterlist) */
			nx842_build_scatterlist(inbuf, max_sync_size, &slin);
			op.in = nx842_get_pa(slin.entries);
			op.inlen = -nx842_get_scatterlist_size(&slin);
		}

		/*
		 * If max_sync_size != NX842_HW_PAGE_SIZE, an indirect
		 * DDE is required for the outbuf.
		 * If max_sync_size == NX842_HW_PAGE_SIZE, outbuf must
		 * also be page aligned (1 in 128/4k=32 chance) in order
		 * to use a direct DDE.
		 * This is unlikely, just use an indirect DDE always.
		 */
		nx842_build_scatterlist(outbuf,
			min(bytesleft, max_sync_size), &slout);
		/* op.out set before loop */
		op.outlen = -nx842_get_scatterlist_size(&slout);

		/* Send request to pHyp */
		ret = vio_h_cop_sync(local_devdata->vdev, &op);

		/* Check for pHyp error */
		if (ret) {
			dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
				__func__, ret, op.hcall_err);
			ret = -EIO;
			goto unlock;
		}

		/* Check for hardware error */
		ret = nx842_validate_result(dev, &csbcpb->csb);
		if (ret && ret != -ENOSPC)
			goto unlock;

		/* Handle incompressible data */
		if (unlikely(ret == -ENOSPC)) {
			if (bytesleft < max_sync_size) {
				/*
				 * Not enough space left in the output buffer
				 * to store uncompressed block
				 */
				goto unlock;
			} else {
				/* Store incompressible block */
				memcpy((void *)outbuf, (void *)inbuf,
					max_sync_size);
				hdr->sizes[i] = -max_sync_size;
				outbuf += max_sync_size;
				bytesleft -= max_sync_size;
				/* Reset ret, incompressible data handled */
				ret = 0;
			}
		} else {
			/* Normal case, compression was successful */
			size = csbcpb->csb.processed_byte_count;
			dev_dbg(dev, "%s: processed_bytes=%d\n",
				__func__, size);
			hdr->sizes[i] = size;
			outbuf += size;
			bytesleft -= size;
		}

		inbuf += max_sync_size;
	}

	*outlen = (unsigned int)(outbuf - (unsigned long)out);

unlock:
	if (ret)
		nx842_inc_comp_failed(local_devdata);
	else {
		nx842_inc_comp_complete(local_devdata);
		ibm_nx842_incr_hist(local_devdata->counters->comp_times,
			(get_tb() - start_time) / tb_ticks_per_usec);
	}
	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(nx842_compress);

static int sw842_decompress(const unsigned char *, int, unsigned char *, int *,
			const void *);

/**
 * nx842_decompress - Decompress data using the 842 algorithm
 *
 * Decompression provide by the NX842 coprocessor on IBM Power systems.
 * The input buffer is decompressed and the result is stored in the
 * provided output buffer.  The size allocated to the output buffer is
 * provided by the caller of this function in @outlen.  Upon return from
 * this function @outlen contains the length of the decompressed data.
 * If there is an error then @outlen will be 0 and an error will be
 * specified by the return code from this function.
 *
 * @in: Pointer to input buffer, will use bounce buffer if not 128 byte
 *      aligned
 * @inlen: Length of input buffer
 * @out: Pointer to output buffer, must be page aligned
 * @outlen: Length of output buffer, must be PAGE_SIZE
 * @wrkmem: ptr to buffer for working memory, size determined by
 *          nx842_get_workmem_size()
 *
 * Returns:
 *   0		Success, output of length @outlen stored in the buffer at @out
 *   -ENODEV	Hardware decompression device is unavailable
 *   -ENOMEM	Unable to allocate internal buffers
 *   -ENOSPC	Output buffer is to small
 *   -EINVAL	Bad input data encountered when attempting decompress
 *   -EIO	Internal error
 */
int nx842_decompress(const unsigned char *in, unsigned int inlen,
			 unsigned char *out, unsigned int *outlen, void *wmem)
{
	struct nx842_header *hdr;
	struct nx842_devdata *local_devdata;
	struct device *dev = NULL;
	struct nx842_workmem *workmem;
	struct nx842_scatterlist slin, slout;
	struct nx_csbcpb *csbcpb;
	int ret = 0, i, size, max_sync_size;
	unsigned long inbuf, outbuf;
	struct vio_pfo_op op = {
		.done = NULL,
		.handle = 0,
		.timeout = 0,
	};
	unsigned long start_time = get_tb();

	/* Ensure page alignment and size */
	outbuf = (unsigned long)out;
	if (!IS_ALIGNED(outbuf, PAGE_SIZE) || *outlen != PAGE_SIZE)
		return -EINVAL;

	rcu_read_lock();
	local_devdata = rcu_dereference(devdata);
	if (local_devdata)
		dev = local_devdata->dev;

	/* Get header */
	hdr = (struct nx842_header *)in;

	workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
		NX842_HW_PAGE_SIZE);

	inbuf = (unsigned long)in + hdr->offset;
	if (likely(!IS_ALIGNED(inbuf, IO_BUFFER_ALIGN))) {
		/* Copy block(s) into bounce buffer for alignment */
		memcpy(workmem->bounce, in + hdr->offset, inlen - hdr->offset);
		inbuf = (unsigned long)workmem->bounce;
	}

	/* Init scatterlist */
	slin.entries = (struct nx842_slentry *)workmem->slin;
	slout.entries = (struct nx842_slentry *)workmem->slout;

	/* Init operation */
	op.flags = NX842_OP_DECOMPRESS;
	csbcpb = &workmem->csbcpb;
	memset(csbcpb, 0, sizeof(*csbcpb));
	op.csbcpb = nx842_get_pa(csbcpb);

	/*
	 * max_sync_size may have changed since compression,
	 * so we can't read it from the device info. We need
	 * to derive it from hdr->blocks_nr.
	 */
	max_sync_size = PAGE_SIZE / hdr->blocks_nr;

	for (i = 0; i < hdr->blocks_nr; i++) {
		/* Skip padding */
		inbuf = ALIGN(inbuf, IO_BUFFER_ALIGN);

		if (hdr->sizes[i] < 0) {
			/* Negative sizes indicate uncompressed data blocks */
			size = abs(hdr->sizes[i]);
			memcpy((void *)outbuf, (void *)inbuf, size);
			outbuf += size;
			inbuf += size;
			continue;
		}

		if (!dev)
			goto sw;

		/*
		 * The better the compression, the more likely the "likely"
		 * case becomes.
		 */
		if (likely((inbuf & NX842_HW_PAGE_MASK) ==
			((inbuf + hdr->sizes[i] - 1) & NX842_HW_PAGE_MASK))) {
			/* Create direct DDE */
			op.in = nx842_get_pa((void *)inbuf);
			op.inlen = hdr->sizes[i];
		} else {
			/* Create indirect DDE (scatterlist) */
			nx842_build_scatterlist(inbuf, hdr->sizes[i] , &slin);
			op.in = nx842_get_pa(slin.entries);
			op.inlen = -nx842_get_scatterlist_size(&slin);
		}

		/*
		 * NOTE: If the default max_sync_size is changed from 4k
		 * to 64k, remove the "likely" case below, since a
		 * scatterlist will always be needed.
		 */
		if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
			/* Create direct DDE */
			op.out = nx842_get_pa((void *)outbuf);
			op.outlen = max_sync_size;
		} else {
			/* Create indirect DDE (scatterlist) */
			nx842_build_scatterlist(outbuf, max_sync_size, &slout);
			op.out = nx842_get_pa(slout.entries);
			op.outlen = -nx842_get_scatterlist_size(&slout);
		}

		/* Send request to pHyp */
		ret = vio_h_cop_sync(local_devdata->vdev, &op);

		/* Check for pHyp error */
		if (ret) {
			dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
				__func__, ret, op.hcall_err);
			dev = NULL;
			goto sw;
		}

		/* Check for hardware error */
		ret = nx842_validate_result(dev, &csbcpb->csb);
		if (ret) {
			dev = NULL;
			goto sw;
		}

		/* HW decompression success */
		inbuf += hdr->sizes[i];
		outbuf += csbcpb->csb.processed_byte_count;
		continue;

sw:
		/* software decompression */
		size = max_sync_size;
		ret = sw842_decompress(
			(unsigned char *)inbuf, hdr->sizes[i],
			(unsigned char *)outbuf, &size, wmem);
		if (ret)
			pr_debug("%s: sw842_decompress failed with %d\n",
				__func__, ret);

		if (ret) {
			if (ret != -ENOSPC && ret != -EINVAL &&
					ret != -EMSGSIZE)
				ret = -EIO;
			goto unlock;
		}

		/* SW decompression success */
		inbuf += hdr->sizes[i];
		outbuf += size;
	}

	*outlen = (unsigned int)(outbuf - (unsigned long)out);

unlock:
	if (ret)
		/* decompress fail */
		nx842_inc_decomp_failed(local_devdata);
	else {
		if (!dev)
			/* software decompress */
			nx842_inc_swdecomp(local_devdata);
		nx842_inc_decomp_complete(local_devdata);
		ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
			(get_tb() - start_time) / tb_ticks_per_usec);
	}

	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(nx842_decompress);

/**
 * nx842_OF_set_defaults -- Set default (disabled) values for devdata
 *
 * @devdata - struct nx842_devdata to update
 *
 * Returns:
 *  0 on success
 *  -ENOENT if @devdata ptr is NULL
 */
static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
{
	if (devdata) {
		devdata->max_sync_size = 0;
		devdata->max_sync_sg = 0;
		devdata->max_sg_len = 0;
		devdata->status = UNAVAILABLE;
		return 0;
	} else
		return -ENOENT;
}

/**
 * nx842_OF_upd_status -- Update the device info from OF status prop
 *
 * The status property indicates if the accelerator is enabled.  If the
 * device is in the OF tree it indicates that the hardware is present.
 * The status field indicates if the device is enabled when the status
 * is 'okay'.  Otherwise the device driver will be disabled.
 *
 * @devdata - struct nx842_devdata to update
 * @prop - struct property point containing the maxsyncop for the update
 *
 * Returns:
 *  0 - Device is available
 *  -EINVAL - Device is not available
 */
static int nx842_OF_upd_status(struct nx842_devdata *devdata,
					struct property *prop) {
	int ret = 0;
	const char *status = (const char *)prop->value;

	if (!strncmp(status, "okay", (size_t)prop->length)) {
		devdata->status = AVAILABLE;
	} else {
		dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n",
				__func__, status);
		devdata->status = UNAVAILABLE;
	}

	return ret;
}

/**
 * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
 *
 * Definition of the 'ibm,max-sg-len' OF property:
 *  This field indicates the maximum byte length of a scatter list
 *  for the platform facility. It is a single cell encoded as with encode-int.
 *
 * Example:
 *  # od -x ibm,max-sg-len
 *  0000000 0000 0ff0
 *
 *  In this example, the maximum byte length of a scatter list is
 *  0x0ff0 (4,080).
 *
 * @devdata - struct nx842_devdata to update
 * @prop - struct property point containing the maxsyncop for the update
 *
 * Returns:
 *  0 on success
 *  -EINVAL on failure
 */
static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
					struct property *prop) {
	int ret = 0;
	const int *maxsglen = prop->value;

	if (prop->length != sizeof(*maxsglen)) {
		dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
		dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
				prop->length, sizeof(*maxsglen));
		ret = -EINVAL;
	} else {
		devdata->max_sg_len = (unsigned int)min(*maxsglen,
				(int)NX842_HW_PAGE_SIZE);
	}

	return ret;
}