summaryrefslogtreecommitdiff
path: root/include/crypto/acompress.h
blob: c497c73baf13ecbf84319c6d99993e02b162f7f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Asynchronous Compression operations
 *
 * Copyright (c) 2016, Intel Corporation
 * Authors: Weigang Li <weigang.li@intel.com>
 *          Giovanni Cabiddu <giovanni.cabiddu@intel.com>
 */
#ifndef _CRYPTO_ACOMP_H
#define _CRYPTO_ACOMP_H

#include <linux/atomic.h>
#include <linux/args.h>
#include <linux/compiler_types.h>
#include <linux/container_of.h>
#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>

/* Set this bit if source is virtual address instead of SG list. */
#define CRYPTO_ACOMP_REQ_SRC_VIRT	0x00000002

/* Set this bit for if virtual address source cannot be used for DMA. */
#define CRYPTO_ACOMP_REQ_SRC_NONDMA	0x00000004

/* Set this bit if destination is virtual address instead of SG list. */
#define CRYPTO_ACOMP_REQ_DST_VIRT	0x00000008

/* Set this bit for if virtual address destination cannot be used for DMA. */
#define CRYPTO_ACOMP_REQ_DST_NONDMA	0x00000010

/* Set this bit if source is a folio. */
#define CRYPTO_ACOMP_REQ_SRC_FOLIO	0x00000020

/* Set this bit if destination is a folio. */
#define CRYPTO_ACOMP_REQ_DST_FOLIO	0x00000040

#define CRYPTO_ACOMP_DST_MAX		131072

#define	MAX_SYNC_COMP_REQSIZE		0

#define ACOMP_REQUEST_ALLOC(name, tfm, gfp) \
        char __##name##_req[sizeof(struct acomp_req) + \
                            MAX_SYNC_COMP_REQSIZE] CRYPTO_MINALIGN_ATTR; \
        struct acomp_req *name = acomp_request_on_stack_init( \
                __##name##_req, (tfm), (gfp), false)

struct acomp_req;
struct folio;

struct acomp_req_chain {
	struct list_head head;
	struct acomp_req *req0;
	struct acomp_req *cur;
	int (*op)(struct acomp_req *req);
	crypto_completion_t compl;
	void *data;
	struct scatterlist ssg;
	struct scatterlist dsg;
	union {
		const u8 *src;
		struct folio *sfolio;
	};
	union {
		u8 *dst;
		struct folio *dfolio;
	};
	size_t soff;
	size_t doff;
	u32 flags;
};

/**
 * struct acomp_req - asynchronous (de)compression request
 *
 * @base:	Common attributes for asynchronous crypto requests
 * @src:	Source scatterlist
 * @dst:	Destination scatterlist
 * @svirt:	Source virtual address
 * @dvirt:	Destination virtual address
 * @sfolio:	Source folio
 * @soff:	Source folio offset
 * @dfolio:	Destination folio
 * @doff:	Destination folio offset
 * @slen:	Size of the input buffer
 * @dlen:	Size of the output buffer and number of bytes produced
 * @chain:	Private API code data, do not use
 * @__ctx:	Start of private context data
 */
struct acomp_req {
	struct crypto_async_request base;
	union {
		struct scatterlist *src;
		const u8 *svirt;
		struct folio *sfolio;
	};
	union {
		struct scatterlist *dst;
		u8 *dvirt;
		struct folio *dfolio;
	};
	size_t soff;
	size_t doff;
	unsigned int slen;
	unsigned int dlen;

	struct acomp_req_chain chain;

	void *__ctx[] CRYPTO_MINALIGN_ATTR;
};

/**
 * struct crypto_acomp - user-instantiated objects which encapsulate
 * algorithms and core processing logic
 *
 * @compress:		Function performs a compress operation
 * @decompress:		Function performs a de-compress operation
 * @reqsize:		Context size for (de)compression requests
 * @fb:			Synchronous fallback tfm
 * @base:		Common crypto API algorithm data structure
 */
struct crypto_acomp {
	int (*compress)(struct acomp_req *req);
	int (*decompress)(struct acomp_req *req);
	unsigned int reqsize;
	struct crypto_acomp *fb;
	struct crypto_tfm base;
};

struct crypto_acomp_stream {
	spinlock_t lock;
	void *ctx;
};

#define COMP_ALG_COMMON {			\
	struct crypto_alg base;			\
	struct crypto_acomp_stream __percpu *stream;	\
}
struct comp_alg_common COMP_ALG_COMMON;

/**
 * DOC: Asynchronous Compression API
 *
 * The Asynchronous Compression API is used with the algorithms of type
 * CRYPTO_ALG_TYPE_ACOMPRESS (listed as type "acomp" in /proc/crypto)
 */

/**
 * crypto_alloc_acomp() -- allocate ACOMPRESS tfm handle
 * @alg_name:	is the cra_name / name or cra_driver_name / driver name of the
 *		compression algorithm e.g. "deflate"
 * @type:	specifies the type of the algorithm
 * @mask:	specifies the mask for the algorithm
 *
 * Allocate a handle for a compression algorithm. The returned struct
 * crypto_acomp is the handle that is required for any subsequent
 * API invocation for the compression operations.
 *
 * Return:	allocated handle in case of success; IS_ERR() is true in case
 *		of an error, PTR_ERR() returns the error code.
 */
struct crypto_acomp *crypto_alloc_acomp(const char *alg_name, u32 type,
					u32 mask);
/**
 * crypto_alloc_acomp_node() -- allocate ACOMPRESS tfm handle with desired NUMA node
 * @alg_name:	is the cra_name / name or cra_driver_name / driver name of the
 *		compression algorithm e.g. "deflate"
 * @type:	specifies the type of the algorithm
 * @mask:	specifies the mask for the algorithm
 * @node:	specifies the NUMA node the ZIP hardware belongs to
 *
 * Allocate a handle for a compression algorithm. Drivers should try to use
 * (de)compressors on the specified NUMA node.
 * The returned struct crypto_acomp is the handle that is required for any
 * subsequent API invocation for the compression operations.
 *
 * Return:	allocated handle in case of success; IS_ERR() is true in case
 *		of an error, PTR_ERR() returns the error code.
 */
struct crypto_acomp *crypto_alloc_acomp_node(const char *alg_name, u32 type,
					u32 mask, int node);

static inline struct crypto_tfm *crypto_acomp_tfm(struct crypto_acomp *tfm)
{
	return &tfm->base;
}

static inline struct comp_alg_common *__crypto_comp_alg_common(
	struct crypto_alg *alg)
{
	return container_of(alg, struct comp_alg_common, base);
}

static inline struct crypto_acomp *__crypto_acomp_tfm(struct crypto_tfm *tfm)
{
	return container_of(tfm, struct crypto_acomp, base);
}

static inline struct comp_alg_common *crypto_comp_alg_common(
	struct crypto_acomp *tfm)
{
	return __crypto_comp_alg_common(crypto_acomp_tfm(tfm)->__crt_alg);
}

static inline unsigned int crypto_acomp_reqsize(struct crypto_acomp *tfm)
{
	return tfm->reqsize;
}

static inline void acomp_request_set_tfm(struct acomp_req *req,
					 struct crypto_acomp *tfm)
{
	req->base.tfm = crypto_acomp_tfm(tfm);
}

static inline bool acomp_is_async(struct crypto_acomp *tfm)
{
	return crypto_comp_alg_common(tfm)->base.cra_flags &
	       CRYPTO_ALG_ASYNC;
}

static inline struct crypto_acomp *crypto_acomp_reqtfm(struct acomp_req *req)
{
	return __crypto_acomp_tfm(req->base.tfm);
}

/**
 * crypto_free_acomp() -- free ACOMPRESS tfm handle
 *
 * @tfm:	ACOMPRESS tfm handle allocated with crypto_alloc_acomp()
 *
 * If @tfm is a NULL or error pointer, this function does nothing.
 */
static inline void crypto_free_acomp(struct crypto_acomp *tfm)
{
	crypto_destroy_tfm(tfm, crypto_acomp_tfm(tfm));
}

static inline int crypto_has_acomp(const char *alg_name, u32 type, u32 mask)
{
	type &= ~CRYPTO_ALG_TYPE_MASK;
	type |= CRYPTO_ALG_TYPE_ACOMPRESS;
	mask |= CRYPTO_ALG_TYPE_ACOMPRESS_MASK;

	return crypto_has_alg(alg_name, type, mask);
}

static inline const char *crypto_acomp_alg_name(struct crypto_acomp *tfm)
{
	return crypto_tfm_alg_name(crypto_acomp_tfm(tfm));
}

static inline const char *crypto_acomp_driver_name(struct crypto_acomp *tfm)
{
	return crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm));
}

/**
 * acomp_request_alloc() -- allocates asynchronous (de)compression request
 *
 * @tfm:	ACOMPRESS tfm handle allocated with crypto_alloc_acomp()
 * @gfp:	gfp to pass to kzalloc (defaults to GFP_KERNEL)
 *
 * Return:	allocated handle in case of success or NULL in case of an error
 */
static inline struct acomp_req *acomp_request_alloc_extra_noprof(
	struct crypto_acomp *tfm, size_t extra, gfp_t gfp)
{
	struct acomp_req *req;
	size_t len;

	len = ALIGN(sizeof(*req) + crypto_acomp_reqsize(tfm), CRYPTO_MINALIGN);
	if (check_add_overflow(len, extra, &len))
		return NULL;

	req = kzalloc_noprof(len, gfp);
	if (likely(req))
		acomp_request_set_tfm(req, tfm);
	return req;
}
#define acomp_request_alloc_noprof(tfm, ...) \
	CONCATENATE(acomp_request_alloc_noprof_, COUNT_ARGS(__VA_ARGS__))( \
		tfm, ##__VA_ARGS__)
#define acomp_request_alloc_noprof_0(tfm) \
	acomp_request_alloc_noprof_1(tfm, GFP_KERNEL)
#define acomp_request_alloc_noprof_1(tfm, gfp) \
	acomp_request_alloc_extra_noprof(tfm, 0, gfp)
#define acomp_request_alloc(...)	alloc_hooks(acomp_request_alloc_noprof(__VA_ARGS__))

/**
 * acomp_request_alloc_extra() -- allocate acomp request with extra memory
 *
 * @tfm:	ACOMPRESS tfm handle allocated with crypto_alloc_acomp()
 * @extra:	amount of extra memory
 * @gfp:	gfp to pass to kzalloc
 *
 * Return:	allocated handle in case of success or NULL in case of an error
 */
#define acomp_request_alloc_extra(...)	alloc_hooks(acomp_request_alloc_extra_noprof(__VA_ARGS__))

static inline void *acomp_request_extra(struct acomp_req *req)
{
	struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);
	size_t len;

	len = ALIGN(sizeof(*req) + crypto_acomp_reqsize(tfm), CRYPTO_MINALIGN);
	return (void *)((char *)req + len);
}

/**
 * acomp_request_free() -- zeroize and free asynchronous (de)compression
 *			   request as well as the output buffer if allocated
 *			   inside the algorithm
 *
 * @req:	request to free
 */
static inline void acomp_request_free(struct acomp_req *req)
{
	if (!req || (req->base.flags & CRYPTO_TFM_REQ_ON_STACK))
		return;
	kfree_sensitive(req);
}

/**
 * acomp_request_set_callback() -- Sets an asynchronous callback
 *
 * Callback will be called when an asynchronous operation on a given
 * request is finished.
 *
 * @req:	request that the callback will be set for
 * @flgs:	specify for instance if the operation may backlog
 * @cmlp:	callback which will be called
 * @data:	private data used by the caller
 */
static inline void acomp_request_set_callback(struct acomp_req *req,
					      u32 flgs,
					      crypto_completion_t cmpl,
					      void *data)
{
	u32 keep = CRYPTO_ACOMP_REQ_SRC_VIRT | CRYPTO_ACOMP_REQ_SRC_NONDMA |
		   CRYPTO_ACOMP_REQ_DST_VIRT | CRYPTO_ACOMP_REQ_DST_NONDMA |
		   CRYPTO_ACOMP_REQ_SRC_FOLIO | CRYPTO_ACOMP_REQ_DST_FOLIO |
		   CRYPTO_TFM_REQ_ON_STACK;

	req->base.complete = cmpl;
	req->base.data = data;
	req->base.flags &= keep;
	req->base.flags |= flgs & ~keep;

	crypto_reqchain_init(&req->base);
}

/**
 * acomp_request_set_params() -- Sets request parameters
 *
 * Sets parameters required by an acomp operation
 *
 * @req:	asynchronous compress request
 * @src:	pointer to input buffer scatterlist
 * @dst:	pointer to output buffer scatterlist. If this is NULL, the
 *		acomp layer will allocate the output memory
 * @slen:	size of the input buffer
 * @dlen:	size of the output buffer. If dst is NULL, this can be used by
 *		the user to specify the maximum amount of memory to allocate
 */
static inline void acomp_request_set_params(struct acomp_req *req,
					    struct scatterlist *src,
					    struct scatterlist *dst,
					    unsigned int slen,
					    unsigned int dlen)
{
	req->src = src;
	req->dst = dst;
	req->slen = slen;
	req->dlen = dlen;

	req->base.flags &= ~(CRYPTO_ACOMP_REQ_SRC_VIRT |
			     CRYPTO_ACOMP_REQ_SRC_NONDMA |
			     CRYPTO_ACOMP_REQ_SRC_FOLIO |
			     CRYPTO_ACOMP_REQ_DST_FOLIO |
			     CRYPTO_ACOMP_REQ_DST_VIRT |
			     CRYPTO_ACOMP_REQ_DST_NONDMA);
}

/**
 * acomp_request_set_src_sg() -- Sets source scatterlist
 *
 * Sets source scatterlist required by an acomp operation.
 *
 * @req:	asynchronous compress request
 * @src:	pointer to input buffer scatterlist
 * @slen:	size of the input buffer
 */
static inline void acomp_request_set_src_sg(struct acomp_req *req,
					    struct scatterlist *src,
					    unsigned int slen)
{
	req->src = src;
	req->slen = slen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_VIRT;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_FOLIO;
}

/**
 * acomp_request_set_src_dma() -- Sets DMA source virtual address
 *
 * Sets source virtual address required by an acomp operation.
 * The address must be usable for DMA.
 *
 * @req:	asynchronous compress request
 * @src:	virtual address pointer to input buffer
 * @slen:	size of the input buffer
 */
static inline void acomp_request_set_src_dma(struct acomp_req *req,
					     const u8 *src, unsigned int slen)
{
	req->svirt = src;
	req->slen = slen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_FOLIO;
	req->base.flags |= CRYPTO_ACOMP_REQ_SRC_VIRT;
}

/**
 * acomp_request_set_src_nondma() -- Sets non-DMA source virtual address
 *
 * Sets source virtual address required by an acomp operation.
 * The address can not be used for DMA.
 *
 * @req:	asynchronous compress request
 * @src:	virtual address pointer to input buffer
 * @slen:	size of the input buffer
 */
static inline void acomp_request_set_src_nondma(struct acomp_req *req,
						const u8 *src,
						unsigned int slen)
{
	req->svirt = src;
	req->slen = slen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_FOLIO;
	req->base.flags |= CRYPTO_ACOMP_REQ_SRC_NONDMA;
	req->base.flags |= CRYPTO_ACOMP_REQ_SRC_VIRT;
}

/**
 * acomp_request_set_src_folio() -- Sets source folio
 *
 * Sets source folio required by an acomp operation.
 *
 * @req:	asynchronous compress request
 * @folio:	pointer to input folio
 * @off:	input folio offset
 * @len:	size of the input buffer
 */
static inline void acomp_request_set_src_folio(struct acomp_req *req,
					       struct folio *folio, size_t off,
					       unsigned int len)
{
	req->sfolio = folio;
	req->soff = off;
	req->slen = len;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_SRC_VIRT;
	req->base.flags |= CRYPTO_ACOMP_REQ_SRC_FOLIO;
}

/**
 * acomp_request_set_dst_sg() -- Sets destination scatterlist
 *
 * Sets destination scatterlist required by an acomp operation.
 *
 * @req:	asynchronous compress request
 * @dst:	pointer to output buffer scatterlist
 * @dlen:	size of the output buffer
 */
static inline void acomp_request_set_dst_sg(struct acomp_req *req,
					    struct scatterlist *dst,
					    unsigned int dlen)
{
	req->dst = dst;
	req->dlen = dlen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_VIRT;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_FOLIO;
}

/**
 * acomp_request_set_dst_dma() -- Sets DMA destination virtual address
 *
 * Sets destination virtual address required by an acomp operation.
 * The address must be usable for DMA.
 *
 * @req:	asynchronous compress request
 * @dst:	virtual address pointer to output buffer
 * @dlen:	size of the output buffer
 */
static inline void acomp_request_set_dst_dma(struct acomp_req *req,
					     u8 *dst, unsigned int dlen)
{
	req->dvirt = dst;
	req->dlen = dlen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_FOLIO;
	req->base.flags |= CRYPTO_ACOMP_REQ_DST_VIRT;
}

/**
 * acomp_request_set_dst_nondma() -- Sets non-DMA destination virtual address
 *
 * Sets destination virtual address required by an acomp operation.
 * The address can not be used for DMA.
 *
 * @req:	asynchronous compress request
 * @dst:	virtual address pointer to output buffer
 * @dlen:	size of the output buffer
 */
static inline void acomp_request_set_dst_nondma(struct acomp_req *req,
						u8 *dst, unsigned int dlen)
{
	req->dvirt = dst;
	req->dlen = dlen;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_FOLIO;
	req->base.flags |= CRYPTO_ACOMP_REQ_DST_NONDMA;
	req->base.flags |= CRYPTO_ACOMP_REQ_DST_VIRT;
}

/**
 * acomp_request_set_dst_folio() -- Sets destination folio
 *
 * Sets destination folio required by an acomp operation.
 *
 * @req:	asynchronous compress request
 * @folio:	pointer to input folio
 * @off:	input folio offset
 * @len:	size of the input buffer
 */
static inline void acomp_request_set_dst_folio(struct acomp_req *req,
					       struct folio *folio, size_t off,
					       unsigned int len)
{
	req->dfolio = folio;
	req->doff = off;
	req->dlen = len;

	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_NONDMA;
	req->base.flags &= ~CRYPTO_ACOMP_REQ_DST_VIRT;
	req->base.flags |= CRYPTO_ACOMP_REQ_DST_FOLIO;
}

static inline void acomp_request_chain(struct acomp_req *req,
				       struct acomp_req *head)
{
	crypto_request_chain(&req->base, &head->base);
}

/**
 * crypto_acomp_compress() -- Invoke asynchronous compress operation
 *
 * Function invokes the asynchronous compress operation
 *
 * @req:	asynchronous compress request
 *
 * Return:	zero on success; error code in case of error
 */
int crypto_acomp_compress(struct acomp_req *req);

/**
 * crypto_acomp_decompress() -- Invoke asynchronous decompress operation
 *
 * Function invokes the asynchronous decompress operation
 *
 * @req:	asynchronous compress request
 *
 * Return:	zero on success; error code in case of error
 */
int crypto_acomp_decompress(struct acomp_req *req);

static inline struct acomp_req *acomp_request_on_stack_init(
	char *buf, struct crypto_acomp *tfm, gfp_t gfp, bool stackonly)
{
	struct acomp_req *req;

	if (!stackonly && (req = acomp_request_alloc(tfm, gfp)))
		return req;

	req = (void *)buf;
	acomp_request_set_tfm(req, tfm->fb);
	req->base.flags = CRYPTO_TFM_REQ_ON_STACK;

	return req;
}

#endif