+ cb->fn = cb_fn;
+ cb->arg = cb_arg;
+
+ /* Add the callbacks in fifo order. */
+ list = &dev->enq_cbs[qp_id];
+ tail = list->next;
+
+ if (tail) {
+ while (tail->next)
+ tail = tail->next;
+ /* Stores to cb->fn and cb->param should complete before
+ * cb is visible to data plane.
+ */
+ __atomic_store_n(&tail->next, cb, __ATOMIC_RELEASE);
+ } else {
+ /* Stores to cb->fn and cb->param should complete before
+ * cb is visible to data plane.
+ */
+ __atomic_store_n(&list->next, cb, __ATOMIC_RELEASE);
+ }
+
+ rte_spinlock_unlock(&rte_cryptodev_callback_lock);
+
+ return cb;
+}
+
+int
+rte_cryptodev_remove_enq_callback(uint8_t dev_id,
+ uint16_t qp_id,
+ struct rte_cryptodev_cb *cb)
+{
+ struct rte_cryptodev *dev;
+ struct rte_cryptodev_cb **prev_cb, *curr_cb;
+ struct rte_cryptodev_cb_rcu *list;
+ int ret;
+
+ ret = -EINVAL;
+
+ if (!cb) {
+ CDEV_LOG_ERR("Callback is NULL");
+ return -EINVAL;
+ }
+
+ if (!rte_cryptodev_pmd_is_valid_dev(dev_id)) {
+ CDEV_LOG_ERR("Invalid dev_id=%d", dev_id);
+ return -ENODEV;
+ }
+
+ dev = &rte_crypto_devices[dev_id];
+ if (qp_id >= dev->data->nb_queue_pairs) {
+ CDEV_LOG_ERR("Invalid queue_pair_id=%d", qp_id);
+ return -ENODEV;
+ }
+
+ rte_spinlock_lock(&rte_cryptodev_callback_lock);
+ if (dev->enq_cbs == NULL) {
+ CDEV_LOG_ERR("Callback not initialized");
+ goto cb_err;
+ }
+
+ list = &dev->enq_cbs[qp_id];
+ if (list == NULL) {
+ CDEV_LOG_ERR("Callback list is NULL");
+ goto cb_err;
+ }
+
+ if (list->qsbr == NULL) {
+ CDEV_LOG_ERR("Rcu qsbr is NULL");
+ goto cb_err;
+ }
+
+ prev_cb = &list->next;
+ for (; *prev_cb != NULL; prev_cb = &curr_cb->next) {
+ curr_cb = *prev_cb;
+ if (curr_cb == cb) {
+ /* Remove the user cb from the callback list. */
+ __atomic_store_n(prev_cb, curr_cb->next,
+ __ATOMIC_RELAXED);
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!ret) {
+ /* Call sync with invalid thread id as this is part of
+ * control plane API
+ */
+ rte_rcu_qsbr_synchronize(list->qsbr, RTE_QSBR_THRID_INVALID);
+ rte_free(cb);
+ }
+
+cb_err:
+ rte_spinlock_unlock(&rte_cryptodev_callback_lock);
+ return ret;
+}
+
+struct rte_cryptodev_cb *
+rte_cryptodev_add_deq_callback(uint8_t dev_id,
+ uint16_t qp_id,
+ rte_cryptodev_callback_fn cb_fn,
+ void *cb_arg)
+{
+ struct rte_cryptodev *dev;
+ struct rte_cryptodev_cb_rcu *list;
+ struct rte_cryptodev_cb *cb, *tail;
+
+ if (!cb_fn) {
+ CDEV_LOG_ERR("Callback is NULL on dev_id=%d", dev_id);
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ if (!rte_cryptodev_pmd_is_valid_dev(dev_id)) {
+ CDEV_LOG_ERR("Invalid dev_id=%d", dev_id);
+ rte_errno = ENODEV;
+ return NULL;
+ }
+
+ dev = &rte_crypto_devices[dev_id];
+ if (qp_id >= dev->data->nb_queue_pairs) {
+ CDEV_LOG_ERR("Invalid queue_pair_id=%d", qp_id);
+ rte_errno = ENODEV;
+ return NULL;
+ }
+
+ cb = rte_zmalloc(NULL, sizeof(*cb), 0);
+ if (cb == NULL) {
+ CDEV_LOG_ERR("Failed to allocate memory for callback on "
+ "dev=%d, queue_pair_id=%d", dev_id, qp_id);
+ rte_errno = ENOMEM;
+ return NULL;
+ }
+
+ rte_spinlock_lock(&rte_cryptodev_callback_lock);
+
+ cb->fn = cb_fn;
+ cb->arg = cb_arg;
+
+ /* Add the callbacks in fifo order. */
+ list = &dev->deq_cbs[qp_id];
+ tail = list->next;
+
+ if (tail) {
+ while (tail->next)
+ tail = tail->next;
+ /* Stores to cb->fn and cb->param should complete before
+ * cb is visible to data plane.
+ */
+ __atomic_store_n(&tail->next, cb, __ATOMIC_RELEASE);
+ } else {
+ /* Stores to cb->fn and cb->param should complete before
+ * cb is visible to data plane.
+ */
+ __atomic_store_n(&list->next, cb, __ATOMIC_RELEASE);
+ }
+
+ rte_spinlock_unlock(&rte_cryptodev_callback_lock);
+
+ return cb;
+}
+
+int
+rte_cryptodev_remove_deq_callback(uint8_t dev_id,
+ uint16_t qp_id,
+ struct rte_cryptodev_cb *cb)
+{
+ struct rte_cryptodev *dev;
+ struct rte_cryptodev_cb **prev_cb, *curr_cb;
+ struct rte_cryptodev_cb_rcu *list;
+ int ret;
+
+ ret = -EINVAL;
+
+ if (!cb) {
+ CDEV_LOG_ERR("Callback is NULL");
+ return -EINVAL;
+ }
+
+ if (!rte_cryptodev_pmd_is_valid_dev(dev_id)) {
+ CDEV_LOG_ERR("Invalid dev_id=%d", dev_id);
+ return -ENODEV;
+ }
+
+ dev = &rte_crypto_devices[dev_id];
+ if (qp_id >= dev->data->nb_queue_pairs) {
+ CDEV_LOG_ERR("Invalid queue_pair_id=%d", qp_id);
+ return -ENODEV;
+ }
+
+ rte_spinlock_lock(&rte_cryptodev_callback_lock);
+ if (dev->enq_cbs == NULL) {
+ CDEV_LOG_ERR("Callback not initialized");
+ goto cb_err;
+ }
+
+ list = &dev->deq_cbs[qp_id];
+ if (list == NULL) {
+ CDEV_LOG_ERR("Callback list is NULL");
+ goto cb_err;
+ }
+
+ if (list->qsbr == NULL) {
+ CDEV_LOG_ERR("Rcu qsbr is NULL");
+ goto cb_err;
+ }
+
+ prev_cb = &list->next;
+ for (; *prev_cb != NULL; prev_cb = &curr_cb->next) {
+ curr_cb = *prev_cb;
+ if (curr_cb == cb) {
+ /* Remove the user cb from the callback list. */
+ __atomic_store_n(prev_cb, curr_cb->next,
+ __ATOMIC_RELAXED);
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!ret) {
+ /* Call sync with invalid thread id as this is part of
+ * control plane API
+ */
+ rte_rcu_qsbr_synchronize(list->qsbr, RTE_QSBR_THRID_INVALID);
+ rte_free(cb);
+ }
+
+cb_err:
+ rte_spinlock_unlock(&rte_cryptodev_callback_lock);
+ return ret;
+}
+
+int
+rte_cryptodev_stats_get(uint8_t dev_id, struct rte_cryptodev_stats *stats)
+{
+ struct rte_cryptodev *dev;
+
+ if (!rte_cryptodev_pmd_is_valid_dev(dev_id)) {
+ CDEV_LOG_ERR("Invalid dev_id=%d", dev_id);
+ return -ENODEV;
+ }
+
+ if (stats == NULL) {
+ CDEV_LOG_ERR("Invalid stats ptr");
+ return -EINVAL;
+ }
+
+ dev = &rte_crypto_devices[dev_id];
+ memset(stats, 0, sizeof(*stats));
+
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->stats_get, -ENOTSUP);
+ (*dev->dev_ops->stats_get)(dev, stats);
+ return 0;