Commit e15bacbe authored by Dan Kruchinin's avatar Dan Kruchinin Committed by Herbert Xu

padata: Make two separate cpumasks

The aim of this patch is to make two separate cpumasks
for padata parallel and serial workers respectively.
It allows user to make more thin and sophisticated configurations
of padata framework. For example user may bind parallel and serial workers to non-intersecting
CPU groups to gain better performance. Also each padata instance has notifiers chain for its
cpumasks now. If either parallel or serial or both masks were changed all
interested subsystems will get notification about that. It's especially useful
if padata user uses algorithm for callback CPU selection according to serial cpumask.
Signed-off-by: 's avatarDan Kruchinin <dkruchinin@acm.org>
Signed-off-by: 's avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 2197f9a1
......@@ -24,12 +24,38 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/notifier.h>
#include <crypto/pcrypt.h>
static struct padata_instance *pcrypt_enc_padata;
static struct padata_instance *pcrypt_dec_padata;
static struct workqueue_struct *encwq;
static struct workqueue_struct *decwq;
struct pcrypt_instance {
struct padata_instance *pinst;
struct workqueue_struct *wq;
/*
* Cpumask for callback CPUs. It should be
* equal to serial cpumask of corresponding padata instance,
* so it is updated when padata notifies us about serial
* cpumask change.
*
* cb_cpumask is protected by RCU. This fact prevents us from
* using cpumask_var_t directly because the actual type of
* cpumsak_var_t depends on kernel configuration(particularly on
* CONFIG_CPUMASK_OFFSTACK macro). Depending on the configuration
* cpumask_var_t may be either a pointer to the struct cpumask
* or a variable allocated on the stack. Thus we can not safely use
* cpumask_var_t with RCU operations such as rcu_assign_pointer or
* rcu_dereference. So cpumask_var_t is wrapped with struct
* pcrypt_cpumask which makes possible to use it with RCU.
*/
struct pcrypt_cpumask {
cpumask_var_t mask;
} *cb_cpumask;
struct notifier_block nblock;
};
static struct pcrypt_instance pencrypt;
static struct pcrypt_instance pdecrypt;
struct pcrypt_instance_ctx {
struct crypto_spawn spawn;
......@@ -42,25 +68,29 @@ struct pcrypt_aead_ctx {
};
static int pcrypt_do_parallel(struct padata_priv *padata, unsigned int *cb_cpu,
struct padata_instance *pinst)
struct pcrypt_instance *pcrypt)
{
unsigned int cpu_index, cpu, i;
struct pcrypt_cpumask *cpumask;
cpu = *cb_cpu;
if (cpumask_test_cpu(cpu, cpu_active_mask))
rcu_read_lock_bh();
cpumask = rcu_dereference(pcrypt->cb_cpumask);
if (cpumask_test_cpu(cpu, cpumask->mask))
goto out;
cpu_index = cpu % cpumask_weight(cpu_active_mask);
cpu_index = cpu % cpumask_weight(cpumask->mask);
cpu = cpumask_first(cpu_active_mask);
cpu = cpumask_first(cpumask->mask);
for (i = 0; i < cpu_index; i++)
cpu = cpumask_next(cpu, cpu_active_mask);
cpu = cpumask_next(cpu, cpumask->mask);
*cb_cpu = cpu;
out:
return padata_do_parallel(pinst, padata, cpu);
rcu_read_unlock_bh();
return padata_do_parallel(pcrypt->pinst, padata, cpu);
}
static int pcrypt_aead_setkey(struct crypto_aead *parent,
......@@ -142,7 +172,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
req->cryptlen, req->iv);
aead_request_set_assoc(creq, req->assoc, req->assoclen);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, pcrypt_enc_padata);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
if (!err)
return -EINPROGRESS;
......@@ -184,7 +214,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
req->cryptlen, req->iv);
aead_request_set_assoc(creq, req->assoc, req->assoclen);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, pcrypt_dec_padata);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pdecrypt);
if (!err)
return -EINPROGRESS;
......@@ -228,7 +258,7 @@ static int pcrypt_aead_givencrypt(struct aead_givcrypt_request *req)
aead_givcrypt_set_assoc(creq, areq->assoc, areq->assoclen);
aead_givcrypt_set_giv(creq, req->giv, req->seq);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, pcrypt_enc_padata);
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
if (!err)
return -EINPROGRESS;
......@@ -370,6 +400,88 @@ static void pcrypt_free(struct crypto_instance *inst)
kfree(inst);
}
static int pcrypt_cpumask_change_notify(struct notifier_block *self,
unsigned long val, void *data)
{
struct pcrypt_instance *pcrypt;
struct pcrypt_cpumask *new_mask, *old_mask;
if (!(val & PADATA_CPU_SERIAL))
return 0;
pcrypt = container_of(self, struct pcrypt_instance, nblock);
new_mask = kmalloc(sizeof(*new_mask), GFP_KERNEL);
if (!new_mask)
return -ENOMEM;
if (!alloc_cpumask_var(&new_mask->mask, GFP_KERNEL)) {
kfree(new_mask);
return -ENOMEM;
}
old_mask = pcrypt->cb_cpumask;
padata_get_cpumask(pcrypt->pinst, PADATA_CPU_SERIAL, new_mask->mask);
rcu_assign_pointer(pcrypt->cb_cpumask, new_mask);
synchronize_rcu_bh();
free_cpumask_var(old_mask->mask);
kfree(old_mask);
return 0;
}
static int __pcrypt_init_instance(struct pcrypt_instance *pcrypt,
const char *name)
{
int ret = -ENOMEM;
struct pcrypt_cpumask *mask;
pcrypt->wq = create_workqueue(name);
if (!pcrypt->wq)
goto err;
pcrypt->pinst = padata_alloc(pcrypt->wq);
if (!pcrypt->pinst)
goto err_destroy_workqueue;
mask = kmalloc(sizeof(*mask), GFP_KERNEL);
if (!mask)
goto err_free_padata;
if (!alloc_cpumask_var(&mask->mask, GFP_KERNEL)) {
kfree(mask);
goto err_free_padata;
}
padata_get_cpumask(pcrypt->pinst, PADATA_CPU_SERIAL, mask->mask);
rcu_assign_pointer(pcrypt->cb_cpumask, mask);
pcrypt->nblock.notifier_call = pcrypt_cpumask_change_notify;
ret = padata_register_cpumask_notifier(pcrypt->pinst, &pcrypt->nblock);
if (ret)
goto err_free_cpumask;
return ret;
err_free_cpumask:
free_cpumask_var(mask->mask);
kfree(mask);
err_free_padata:
padata_free(pcrypt->pinst);
err_destroy_workqueue:
destroy_workqueue(pcrypt->wq);
err:
return ret;
}
static void __pcrypt_deinit_instance(struct pcrypt_instance *pcrypt)
{
free_cpumask_var(pcrypt->cb_cpumask->mask);
kfree(pcrypt->cb_cpumask);
padata_stop(pcrypt->pinst);
padata_unregister_cpumask_notifier(pcrypt->pinst, &pcrypt->nblock);
destroy_workqueue(pcrypt->wq);
padata_free(pcrypt->pinst);
}
static struct crypto_template pcrypt_tmpl = {
.name = "pcrypt",
.alloc = pcrypt_alloc,
......@@ -379,60 +491,31 @@ static struct crypto_template pcrypt_tmpl = {
static int __init pcrypt_init(void)
{
int err = -ENOMEM;
encwq = create_workqueue("pencrypt");
if (!encwq)
goto err;
decwq = create_workqueue("pdecrypt");
if (!decwq)
goto err_destroy_encwq;
pcrypt_enc_padata = padata_alloc(cpu_possible_mask, encwq);
if (!pcrypt_enc_padata)
goto err_destroy_decwq;
pcrypt_dec_padata = padata_alloc(cpu_possible_mask, decwq);
if (!pcrypt_dec_padata)
goto err_free_enc_padata;
int err;
err = padata_start(pcrypt_enc_padata);
err = __pcrypt_init_instance(&pencrypt, "pencrypt");
if (err)
goto err_free_dec_padata;
goto err;
err = padata_start(pcrypt_dec_padata);
err = __pcrypt_init_instance(&pdecrypt, "pdecrypt");
if (err)
goto err_free_dec_padata;
return crypto_register_template(&pcrypt_tmpl);
err_free_dec_padata:
padata_free(pcrypt_dec_padata);
goto err_deinit_pencrypt;
err_free_enc_padata:
padata_free(pcrypt_enc_padata);
padata_start(pencrypt.pinst);
padata_start(pdecrypt.pinst);
err_destroy_decwq:
destroy_workqueue(decwq);
err_destroy_encwq:
destroy_workqueue(encwq);
return crypto_register_template(&pcrypt_tmpl);
err_deinit_pencrypt:
__pcrypt_deinit_instance(&pencrypt);
err:
return err;
}
static void __exit pcrypt_exit(void)
{
padata_stop(pcrypt_enc_padata);
padata_stop(pcrypt_dec_padata);
destroy_workqueue(encwq);
destroy_workqueue(decwq);
padata_free(pcrypt_enc_padata);
padata_free(pcrypt_dec_padata);
__pcrypt_deinit_instance(&pencrypt);
__pcrypt_deinit_instance(&pdecrypt);
crypto_unregister_template(&pcrypt_tmpl);
}
......
......@@ -25,6 +25,10 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/notifier.h>
#define PADATA_CPU_SERIAL 0x01
#define PADATA_CPU_PARALLEL 0x02
/**
* struct padata_priv - Embedded to the users data structure.
......@@ -59,7 +63,20 @@ struct padata_list {
};
/**
* struct padata_queue - The percpu padata queues.
* struct padata_serial_queue - The percpu padata serial queue
*
* @serial: List to wait for serialization after reordering.
* @work: work struct for serialization.
* @pd: Backpointer to the internal control structure.
*/
struct padata_serial_queue {
struct padata_list serial;
struct work_struct work;
struct parallel_data *pd;
};
/**
* struct padata_parallel_queue - The percpu padata parallel queue
*
* @parallel: List to wait for parallelization.
* @reorder: List to wait for reordering after parallel processing.
......@@ -67,44 +84,52 @@ struct padata_list {
* @pwork: work struct for parallelization.
* @swork: work struct for serialization.
* @pd: Backpointer to the internal control structure.
* @work: work struct for parallelization.
* @num_obj: Number of objects that are processed by this cpu.
* @cpu_index: Index of the cpu.
*/
struct padata_queue {
struct padata_list parallel;
struct padata_list reorder;
struct padata_list serial;
struct work_struct pwork;
struct work_struct swork;
struct parallel_data *pd;
int cpu_index;
struct padata_parallel_queue {
struct padata_list parallel;
struct padata_list reorder;
struct parallel_data *pd;
struct work_struct work;
atomic_t num_obj;
int cpu_index;
};
/**
* struct parallel_data - Internal control structure, covers everything
* that depends on the cpumask in use.
*
* @pinst: padata instance.
* @queue: percpu padata queues.
* @pqueue: percpu padata queues used for parallelization.
* @squeue: percpu padata queues used for serialuzation.
* @seq_nr: The sequence number that will be attached to the next object.
* @reorder_objects: Number of objects waiting in the reorder queues.
* @refcnt: Number of objects holding a reference on this parallel_data.
* @max_seq_nr: Maximal used sequence number.
* @cpumask: cpumask in use.
* @cpumask: Contains two cpumasks: pcpu and cbcpu for
* parallel and serial workers respectively.
* @lock: Reorder lock.
* @processed: Number of already processed objects.
* @timer: Reorder timer.
*/
struct parallel_data {
struct padata_instance *pinst;
struct padata_queue *queue;
atomic_t seq_nr;
atomic_t reorder_objects;
atomic_t refcnt;
unsigned int max_seq_nr;
cpumask_var_t cpumask;
spinlock_t lock ____cacheline_aligned;
unsigned int processed;
struct timer_list timer;
struct padata_instance *pinst;
struct padata_parallel_queue *pqueue;
struct padata_serial_queue *squeue;
atomic_t seq_nr;
atomic_t reorder_objects;
atomic_t refcnt;
unsigned int max_seq_nr;
struct {
cpumask_var_t pcpu;
cpumask_var_t cbcpu;
} cpumask;
spinlock_t lock ____cacheline_aligned;
unsigned int processed;
struct timer_list timer;
};
/**
......@@ -113,32 +138,51 @@ struct parallel_data {
* @cpu_notifier: cpu hotplug notifier.
* @wq: The workqueue in use.
* @pd: The internal control structure.
* @cpumask: User supplied cpumask.
* @cpumask: User supplied cpumask. Contains two cpumasks: pcpu and
* cbcpu for parallel and serial works respectivly.
* @cpumask_change_notifier: Notifiers chain for user-defined notify
* callbacks that will be called when either @pcpu or @cbcpu
* or both cpumasks change.
* @lock: padata instance lock.
* @flags: padata flags.
*/
struct padata_instance {
struct notifier_block cpu_notifier;
struct workqueue_struct *wq;
struct parallel_data *pd;
cpumask_var_t cpumask;
struct mutex lock;
u8 flags;
#define PADATA_INIT 1
#define PADATA_RESET 2
#define PADATA_INVALID 4
struct notifier_block cpu_notifier;
struct workqueue_struct *wq;
struct parallel_data *pd;
struct {
cpumask_var_t pcpu;
cpumask_var_t cbcpu;
} cpumask;
struct blocking_notifier_head cpumask_change_notifier;
struct mutex lock;
u8 flags;
#define PADATA_INIT 1
#define PADATA_RESET 2
#define PADATA_INVALID 4
};
extern struct padata_instance *padata_alloc(const struct cpumask *cpumask,
struct workqueue_struct *wq);
extern struct padata_instance *padata_alloc(struct workqueue_struct *wq);
extern struct padata_instance *__padata_alloc(struct workqueue_struct *wq,
const struct cpumask *pcpumask,
const struct cpumask *cbcpumask);
extern void padata_free(struct padata_instance *pinst);
extern int padata_do_parallel(struct padata_instance *pinst,
struct padata_priv *padata, int cb_cpu);
extern void padata_do_serial(struct padata_priv *padata);
extern int padata_set_cpumask(struct padata_instance *pinst,
extern int padata_get_cpumask(struct padata_instance *pinst,
int cpumask_type, struct cpumask *out_mask);
extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
cpumask_var_t cpumask);
extern int padata_add_cpu(struct padata_instance *pinst, int cpu);
extern int padata_remove_cpu(struct padata_instance *pinst, int cpu);
extern int __padata_set_cpumasks(struct padata_instance *pinst,
cpumask_var_t pcpumask,
cpumask_var_t cbcpumask);
extern int padata_add_cpu(struct padata_instance *pinst, int cpu, int mask);
extern int padata_remove_cpu(struct padata_instance *pinst, int cpu, int mask);
extern int padata_start(struct padata_instance *pinst);
extern void padata_stop(struct padata_instance *pinst);
extern int padata_register_cpumask_notifier(struct padata_instance *pinst,
struct notifier_block *nblock);
extern int padata_unregister_cpumask_notifier(struct padata_instance *pinst,
struct notifier_block *nblock);
#endif
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment