@@ -53,6 +53,7 @@ struct ib_update_work {
struct work_struct work;
struct ib_device *device;
u8 port_num;
+ bool enforce_security;
};
union ib_gid zgid;
@@ -1037,7 +1038,8 @@ int ib_get_cached_lmc(struct ib_device *device,
EXPORT_SYMBOL(ib_get_cached_lmc);
static void ib_cache_update(struct ib_device *device,
- u8 port)
+ u8 port,
+ bool enforce_security)
{
struct ib_port_attr *tprops = NULL;
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
@@ -1125,6 +1127,11 @@ static void ib_cache_update(struct ib_device *device,
tprops->subnet_prefix;
write_unlock_irq(&device->cache.lock);
+ if (enforce_security)
+ ib_security_cache_change(device,
+ port,
+ tprops->subnet_prefix);
+
kfree(gid_cache);
kfree(old_pkey_cache);
kfree(tprops);
@@ -1141,7 +1148,9 @@ static void ib_cache_task(struct work_struct *_work)
struct ib_update_work *work =
container_of(_work, struct ib_update_work, work);
- ib_cache_update(work->device, work->port_num);
+ ib_cache_update(work->device,
+ work->port_num,
+ work->enforce_security);
kfree(work);
}
@@ -1162,6 +1171,12 @@ static void ib_cache_event(struct ib_event_handler *handler,
INIT_WORK(&work->work, ib_cache_task);
work->device = event->device;
work->port_num = event->element.port_num;
+ if (event->event == IB_EVENT_PKEY_CHANGE ||
+ event->event == IB_EVENT_GID_CHANGE)
+ work->enforce_security = true;
+ else
+ work->enforce_security = false;
+
queue_work(ib_wq, &work->work);
}
}
@@ -1204,7 +1219,7 @@ int ib_cache_setup_one(struct ib_device *device)
return err;
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
- ib_cache_update(device, p + rdma_start_port(device));
+ ib_cache_update(device, p + rdma_start_port(device), true);
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
device, ib_cache_event);
@@ -38,6 +38,14 @@
#include <rdma/ib_verbs.h>
+struct pkey_index_qp_list {
+ struct list_head pkey_index_list;
+ u16 pkey_index;
+ /* Lock to hold while iterating the qp_list. */
+ spinlock_t qp_list_lock;
+ struct list_head qp_list;
+};
+
#if IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS)
int cma_configfs_init(void);
void cma_configfs_exit(void);
@@ -147,14 +155,22 @@ int ib_security_enforce_mad_agent_pkey_access(struct ib_device *dev,
u16 pkey_index,
struct ib_mad_agent *mad_agent);
+void ib_security_destroy_port_pkey_list(struct ib_device *device);
+
+void ib_security_cache_change(struct ib_device *device,
+ u8 port_num,
+ u64 subnet_prefix);
+
int ib_security_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_udata *udata);
-int ib_security_create_qp_security(struct ib_qp *qp);
-void ib_security_destroy_qp(struct ib_qp_security *sec);
-int ib_security_open_shared_qp(struct ib_qp *qp);
+int ib_security_create_qp_security(struct ib_qp *qp, struct ib_device *dev);
+void ib_security_destroy_qp_begin(struct ib_qp_security *sec);
+void ib_security_destroy_qp_abort(struct ib_qp_security *sec);
+void ib_security_destroy_qp_end(struct ib_qp_security *sec);
+int ib_security_open_shared_qp(struct ib_qp *qp, struct ib_device *dev);
void ib_security_close_shared_qp(struct ib_qp_security *sec);
#else
static inline int ib_security_enforce_mad_agent_pkey_access(
@@ -166,6 +182,16 @@ static inline int ib_security_enforce_mad_agent_pkey_access(
return 0;
}
+static inline void ib_security_destroy_port_pkey_list(struct ib_device *device)
+{
+}
+
+static inline void ib_security_cache_change(struct ib_device *device,
+ u8 port_num,
+ u64 subnet_prefix)
+{
+}
+
static inline int ib_security_modify_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask,
@@ -177,16 +203,26 @@ static inline int ib_security_modify_qp(struct ib_qp *qp,
udata);
}
-static inline int ib_security_create_qp_security(struct ib_qp *qp)
+static inline int ib_security_create_qp_security(struct ib_qp *qp,
+ struct ib_device *dev)
{
return 0;
}
-static inline void ib_security_destroy_qp(struct ib_qp_security *sec)
+static inline void ib_security_destroy_qp_begin(struct ib_qp_security *sec)
+{
+}
+
+static inline void ib_security_destroy_qp_abort(struct ib_qp_security *sec)
+{
+}
+
+static inline void ib_security_destroy_qp_end(struct ib_qp_security *sec)
{
}
-static inline int ib_security_open_shared_qp(struct ib_qp *qp)
+static inline int ib_security_open_shared_qp(struct ib_qp *qp,
+ struct ib_device *dev)
{
return 0;
}
@@ -1,4 +1,5 @@
-/*
+/* NEW COMMIT TO INSERT INTO REBASE
+ *
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
*
* This software is available to you under a choice of one of two
@@ -33,11 +34,27 @@
#ifdef CONFIG_SECURITY_INFINIBAND
#include <linux/security.h>
+#include <linux/list.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
#include "core_priv.h"
+static struct pkey_index_qp_list *get_pkey_index_qp_list(struct ib_device *dev,
+ u8 port_num,
+ u16 index)
+{
+ struct pkey_index_qp_list *tmp_pkey;
+
+ list_for_each_entry(tmp_pkey,
+ &dev->port_pkey_list[port_num].pkey_list,
+ pkey_index_list) {
+ if (tmp_pkey->pkey_index == index)
+ return tmp_pkey;
+ }
+ return NULL;
+}
+
static int get_pkey_info(struct ib_device *dev,
u8 port_num,
u16 pkey_index,
@@ -90,6 +107,175 @@ static int enforce_qp_pkey_security(struct ib_device *dev,
return err;
}
+static int check_qp_port_pkey_settings(struct ib_qp_security *sec)
+{
+ struct ib_qp *real_qp = sec->qp->real_qp;
+ int err = 0;
+
+ if (real_qp->qp_sec->ports_pkeys.main.state != IB_PORT_PKEY_NOT_VALID)
+ err = enforce_qp_pkey_security(real_qp->device,
+ real_qp->qp_sec->ports_pkeys.main.port_num,
+ real_qp->qp_sec->ports_pkeys.main.pkey_index,
+ sec);
+ if (err)
+ goto out;
+
+ if (real_qp->qp_sec->ports_pkeys.alt.state != IB_PORT_PKEY_NOT_VALID)
+ err = enforce_qp_pkey_security(real_qp->device,
+ real_qp->qp_sec->ports_pkeys.alt.port_num,
+ real_qp->qp_sec->ports_pkeys.alt.pkey_index,
+ sec);
+
+ if (err)
+ goto out;
+
+out:
+ return err;
+}
+
+static void reset_qp(struct ib_qp_security *sec)
+{
+ struct ib_qp_security *shared_qp_sec;
+ struct ib_qp_attr attr = {
+ .qp_state = IB_QPS_ERR
+ };
+ struct ib_event event = {
+ .event = IB_EVENT_QP_FATAL
+ };
+
+ mutex_lock(&sec->mutex);
+ if (sec->destroying)
+ goto unlock;
+
+ ib_modify_qp(sec->qp,
+ &attr,
+ IB_QP_STATE);
+
+ if (sec->qp->event_handler && sec->qp->qp_context) {
+ event.element.qp = sec->qp;
+ sec->qp->event_handler(&event,
+ sec->qp->qp_context);
+ }
+
+ list_for_each_entry(shared_qp_sec,
+ &sec->shared_qp_list,
+ shared_qp_list) {
+ struct ib_qp *qp = shared_qp_sec->qp;
+
+ if (qp->event_handler && qp->qp_context) {
+ event.element.qp = qp;
+ event.device = qp->device;
+ qp->event_handler(&event,
+ qp->qp_context);
+ }
+ }
+unlock:
+ mutex_unlock(&sec->mutex);
+}
+
+static inline void check_pkey_qps(struct pkey_index_qp_list *pkey,
+ struct ib_device *device,
+ u8 port_num,
+ u64 subnet_prefix)
+{
+ struct ib_qp_security *shared_qp_sec;
+ struct ib_port_pkey *pp, *tmp_pp;
+ LIST_HEAD(reset_list);
+ u16 pkey_val;
+
+ if (!ib_get_cached_pkey(device,
+ port_num,
+ pkey->pkey_index,
+ &pkey_val)) {
+ spin_lock(&pkey->qp_list_lock);
+ list_for_each_entry(pp, &pkey->qp_list, qp_list) {
+ if (pp->sec->destroying)
+ continue;
+
+ if (security_qp_pkey_access(subnet_prefix,
+ pkey_val,
+ pp->sec)) {
+ list_add(&pp->reset_list,
+ &reset_list);
+ } else {
+ list_for_each_entry(shared_qp_sec,
+ &pp->sec->shared_qp_list,
+ shared_qp_list) {
+ if (security_qp_pkey_access(subnet_prefix,
+ pkey_val,
+ shared_qp_sec)) {
+ list_add(&pp->reset_list,
+ &reset_list);
+ break;
+ }
+ }
+ }
+ }
+ spin_unlock(&pkey->qp_list_lock);
+ }
+
+ list_for_each_entry_safe(pp,
+ tmp_pp,
+ &reset_list,
+ reset_list) {
+ reset_qp(pp->sec);
+ list_del(&pp->reset_list);
+ }
+}
+
+static int port_pkey_list_insert(struct ib_port_pkey *pp,
+ u8 port_num,
+ u16 index)
+{
+ struct pkey_index_qp_list *pkey;
+ struct ib_device *device = pp->sec->dev;
+ int err = 0;
+
+ spin_lock(&device->port_pkey_list[port_num].list_lock);
+ pkey = get_pkey_index_qp_list(pp->sec->dev, port_num, index);
+ if (pkey)
+ goto list_qp;
+
+ pkey = kzalloc(sizeof(*pkey), GFP_ATOMIC);
+ if (!pkey) {
+ spin_unlock(&device->port_pkey_list[port_num].list_lock);
+ return -ENOMEM;
+ }
+
+ pkey->pkey_index = index;
+ spin_lock_init(&pkey->qp_list_lock);
+ INIT_LIST_HEAD(&pkey->qp_list);
+ list_add(&pkey->pkey_index_list,
+ &device->port_pkey_list[port_num].pkey_list);
+
+list_qp:
+ spin_unlock(&device->port_pkey_list[port_num].list_lock);
+
+ spin_lock(&pkey->qp_list_lock);
+ list_add(&pp->qp_list, &pkey->qp_list);
+ spin_unlock(&pkey->qp_list_lock);
+
+ return err;
+}
+
+static int port_pkey_list_remove(struct ib_port_pkey *pp)
+{
+ struct pkey_index_qp_list *pkey;
+ int err = 0;
+
+ pkey = get_pkey_index_qp_list(pp->sec->dev,
+ pp->port_num,
+ pp->pkey_index);
+ if (!pkey)
+ return -ENOENT;
+
+ spin_lock(&pkey->qp_list_lock);
+ list_del(&pp->qp_list);
+ pp->state = IB_PORT_PKEY_NOT_VALID;
+ spin_unlock(&pkey->qp_list_lock);
+ return err;
+}
+
static int check_pkey(const struct ib_qp *qp,
const struct ib_qp_attr *qp_attr,
int qp_attr_mask)
@@ -118,15 +304,23 @@ static int affects_security_settings(const struct ib_qp *qp,
check_alt_pkey(qp, qp_attr, qp_attr_mask);
}
-static void begin_port_pkey_change(struct ib_qp *qp,
- struct ib_port_pkey *pp,
- struct ib_port_pkey *old_pp,
- u8 port_num,
- u16 pkey_index)
+static int begin_port_pkey_change(struct ib_port_pkey *pp,
+ struct ib_port_pkey *old_pp,
+ u8 port_num,
+ u16 pkey_index)
{
+ int err;
+
if (pp->state == IB_PORT_PKEY_NOT_VALID ||
(pkey_index != pp->pkey_index ||
port_num != pp->port_num)) {
+ err = port_pkey_list_insert(pp,
+ port_num,
+ pkey_index);
+
+ if (err)
+ return err;
+
old_pp->pkey_index = pp->pkey_index;
old_pp->port_num = pp->port_num;
old_pp->state = pp->state;
@@ -135,6 +329,7 @@ static void begin_port_pkey_change(struct ib_qp *qp,
pp->pkey_index = pkey_index;
pp->state = IB_PORT_PKEY_CHANGING;
}
+ return 0;
}
static int qp_modify_enforce_security(struct ib_qp *qp,
@@ -161,11 +356,12 @@ static int qp_modify_enforce_security(struct ib_qp *qp,
if (err)
return err;
- begin_port_pkey_change(qp,
- &sec->ports_pkeys.main,
- &sec->old_ports_pkeys.main,
- port_num,
- pkey_index);
+ err = begin_port_pkey_change(&sec->ports_pkeys.main,
+ &sec->old_ports_pkeys.main,
+ port_num,
+ pkey_index);
+ if (err)
+ return err;
}
if (check_alt_pkey(qp, qp_attr, qp_attr_mask)) {
@@ -177,76 +373,123 @@ static int qp_modify_enforce_security(struct ib_qp *qp,
if (err)
return err;
- begin_port_pkey_change(qp,
- &sec->ports_pkeys.alt,
- &sec->old_ports_pkeys.alt,
- qp_attr->alt_port_num,
- qp_attr->alt_pkey_index);
+ err = begin_port_pkey_change(&sec->ports_pkeys.alt,
+ &sec->old_ports_pkeys.alt,
+ qp_attr->alt_port_num,
+ qp_attr->alt_pkey_index);
}
return err;
}
-static void abort_port_pkey_change(struct ib_qp *qp,
- struct ib_port_pkey *pp,
+static void abort_port_pkey_change(struct ib_port_pkey *pp,
struct ib_port_pkey *old_pp)
{
if (pp->state == IB_PORT_PKEY_CHANGING) {
+ port_pkey_list_remove(pp);
+
pp->pkey_index = old_pp->pkey_index;
pp->port_num = old_pp->port_num;
pp->state = old_pp->state;
}
}
+static void end_port_pkey_change(struct ib_port_pkey *pp,
+ struct ib_port_pkey *old_pp)
+{
+ if (pp->state == IB_PORT_PKEY_CHANGING)
+ pp->state = IB_PORT_PKEY_VALID;
+
+ if (old_pp->state == IB_PORT_PKEY_VALID)
+ port_pkey_list_remove(old_pp);
+}
+
static int cleanup_qp_pkey_associations(struct ib_qp *qp,
bool revert_to_old)
{
- struct ib_qp_security *sec = qp->qp_sec;
-
if (revert_to_old) {
- abort_port_pkey_change(qp,
- &qp->qp_sec->ports_pkeys.main,
+ abort_port_pkey_change(&qp->qp_sec->ports_pkeys.main,
&qp->qp_sec->old_ports_pkeys.main);
- abort_port_pkey_change(qp,
- &qp->qp_sec->ports_pkeys.alt,
+ abort_port_pkey_change(&qp->qp_sec->ports_pkeys.alt,
&qp->qp_sec->old_ports_pkeys.alt);
} else {
- if (sec->ports_pkeys.main.state == IB_PORT_PKEY_CHANGING)
- sec->ports_pkeys.main.state = IB_PORT_PKEY_VALID;
+ end_port_pkey_change(&qp->qp_sec->ports_pkeys.main,
+ &qp->qp_sec->old_ports_pkeys.main);
- if (sec->ports_pkeys.alt.state == IB_PORT_PKEY_CHANGING)
- sec->ports_pkeys.alt.state = IB_PORT_PKEY_VALID;
+ end_port_pkey_change(&qp->qp_sec->ports_pkeys.alt,
+ &qp->qp_sec->old_ports_pkeys.alt);
}
- memset(&sec->old_ports_pkeys, 0, sizeof(sec->old_ports_pkeys));
-
return 0;
}
-int ib_security_open_shared_qp(struct ib_qp *qp)
+static void destroy_qp_security(struct ib_qp_security *sec)
+{
+ security_ib_qp_free_security(sec);
+ kfree(sec);
+}
+
+static void qp_lists_lock_unlock(struct ib_qp_security *sec,
+ bool lock)
+{
+ struct ib_port_pkey *prim = NULL;
+ struct ib_port_pkey *alt = NULL;
+ struct ib_port_pkey *first = NULL;
+ struct ib_port_pkey *second = NULL;
+ struct pkey_index_qp_list *pkey;
+
+ if (sec->ports_pkeys.main.state != IB_PORT_PKEY_NOT_VALID)
+ prim = &sec->ports_pkeys.main;
+
+ if (sec->ports_pkeys.alt.state != IB_PORT_PKEY_NOT_VALID)
+ alt = &sec->ports_pkeys.alt;
+
+ if (prim && alt) {
+ if (prim->port_num != alt->port_num) {
+ first = prim->port_num < alt->port_num ? prim : alt;
+ second = prim->port_num >= alt->port_num ? prim : alt;
+ } else {
+ first = prim->pkey_index < alt->pkey_index ?
+ prim : alt;
+ second = prim->pkey_index >= alt->pkey_index ?
+ alt : prim;
+ }
+ } else {
+ first = !prim ? alt : prim;
+ }
+
+ if (first) {
+ pkey = get_pkey_index_qp_list(sec->dev,
+ first->port_num,
+ first->pkey_index);
+ if (lock)
+ spin_lock(&pkey->qp_list_lock);
+ else
+ spin_unlock(&pkey->qp_list_lock);
+ }
+
+ if (second) {
+ pkey = get_pkey_index_qp_list(sec->dev,
+ second->port_num,
+ second->pkey_index);
+ if (lock)
+ spin_lock(&pkey->qp_list_lock);
+ else
+ spin_unlock(&pkey->qp_list_lock);
+ }
+}
+
+int ib_security_open_shared_qp(struct ib_qp *qp, struct ib_device *dev)
{
struct ib_qp *real_qp = qp->real_qp;
int err;
- err = ib_security_create_qp_security(qp);
+ err = ib_security_create_qp_security(qp, dev);
if (err)
goto out;
mutex_lock(&real_qp->qp_sec->mutex);
-
- if (real_qp->qp_sec->ports_pkeys.main.state != IB_PORT_PKEY_NOT_VALID)
- err = enforce_qp_pkey_security(real_qp->device,
- real_qp->qp_sec->ports_pkeys.main.port_num,
- real_qp->qp_sec->ports_pkeys.main.pkey_index,
- qp->qp_sec);
- if (err)
- goto err;
-
- if (real_qp->qp_sec->ports_pkeys.alt.state != IB_PORT_PKEY_NOT_VALID)
- err = enforce_qp_pkey_security(real_qp->device,
- real_qp->qp_sec->ports_pkeys.alt.port_num,
- real_qp->qp_sec->ports_pkeys.alt.pkey_index,
- qp->qp_sec);
+ err = check_qp_port_pkey_settings(qp->qp_sec);
if (err)
goto err;
@@ -257,7 +500,7 @@ int ib_security_open_shared_qp(struct ib_qp *qp)
err:
mutex_unlock(&real_qp->qp_sec->mutex);
if (err)
- ib_security_destroy_qp(qp->qp_sec);
+ destroy_qp_security(qp->qp_sec);
out:
return err;
@@ -271,10 +514,10 @@ void ib_security_close_shared_qp(struct ib_qp_security *sec)
list_del(&sec->shared_qp_list);
mutex_unlock(&real_qp->qp_sec->mutex);
- ib_security_destroy_qp(sec);
+ destroy_qp_security(sec);
}
-int ib_security_create_qp_security(struct ib_qp *qp)
+int ib_security_create_qp_security(struct ib_qp *qp, struct ib_device *dev)
{
int err;
@@ -283,6 +526,9 @@ int ib_security_create_qp_security(struct ib_qp *qp)
return -ENOMEM;
qp->qp_sec->qp = qp;
+ qp->qp_sec->dev = dev;
+ qp->qp_sec->ports_pkeys.main.sec = qp->qp_sec;
+ qp->qp_sec->ports_pkeys.alt.sec = qp->qp_sec;
mutex_init(&qp->qp_sec->mutex);
INIT_LIST_HEAD(&qp->qp_sec->shared_qp_list);
err = security_ib_qp_alloc_security(qp->qp_sec);
@@ -293,10 +539,86 @@ int ib_security_create_qp_security(struct ib_qp *qp)
}
EXPORT_SYMBOL(ib_security_create_qp_security);
-void ib_security_destroy_qp(struct ib_qp_security *sec)
+void ib_security_destroy_qp_end(struct ib_qp_security *sec)
{
- security_ib_qp_free_security(sec);
- kfree(sec);
+ mutex_lock(&sec->mutex);
+ if (sec->ports_pkeys.main.state != IB_PORT_PKEY_NOT_VALID)
+ port_pkey_list_remove(&sec->ports_pkeys.main);
+
+ if (sec->ports_pkeys.alt.state != IB_PORT_PKEY_NOT_VALID)
+ port_pkey_list_remove(&sec->ports_pkeys.alt);
+
+ memset(&sec->ports_pkeys, 0, sizeof(sec->ports_pkeys));
+ mutex_unlock(&sec->mutex);
+ destroy_qp_security(sec);
+}
+
+void ib_security_destroy_qp_abort(struct ib_qp_security *sec)
+{
+ int err;
+
+ mutex_lock(&sec->mutex);
+ qp_lists_lock_unlock(sec, true);
+ err = check_qp_port_pkey_settings(sec);
+ if (err)
+ reset_qp(sec);
+ sec->destroying = false;
+ qp_lists_lock_unlock(sec, false);
+ mutex_unlock(&sec->mutex);
+}
+
+void ib_security_destroy_qp_begin(struct ib_qp_security *sec)
+{
+ mutex_lock(&sec->mutex);
+ qp_lists_lock_unlock(sec, true);
+ sec->destroying = true;
+ qp_lists_lock_unlock(sec, false);
+ mutex_unlock(&sec->mutex);
+}
+
+void ib_security_cache_change(struct ib_device *device,
+ u8 port_num,
+ u64 subnet_prefix)
+{
+ struct pkey_index_qp_list *pkey;
+
+ list_for_each_entry(pkey,
+ &device->port_pkey_list[port_num].pkey_list,
+ pkey_index_list) {
+ check_pkey_qps(pkey,
+ device,
+ port_num,
+ subnet_prefix);
+ }
+}
+
+void ib_security_destroy_port_pkey_list(struct ib_device *device)
+{
+ struct pkey_index_qp_list *pkey, *tmp_pkey;
+ struct ib_port_pkey *pp, *tmp_pp;
+ int i;
+
+ for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
+ spin_lock(&device->port_pkey_list[i].list_lock);
+ list_for_each_entry_safe(pkey,
+ tmp_pkey,
+ &device->port_pkey_list[i].pkey_list,
+ pkey_index_list) {
+ spin_lock(&pkey->qp_list_lock);
+ list_for_each_entry_safe(pp,
+ tmp_pp,
+ &pkey->qp_list,
+ qp_list) {
+ if (pp->state != IB_PORT_PKEY_NOT_VALID)
+ list_del(&pp->qp_list);
+ }
+ spin_unlock(&pkey->qp_list_lock);
+
+ list_del(&pkey->pkey_index_list);
+ kfree(pkey);
+ }
+ spin_unlock(&device->port_pkey_list[i].list_lock);
+ }
}
int ib_security_modify_qp(struct ib_qp *qp,
@@ -311,7 +633,6 @@ int ib_security_modify_qp(struct ib_qp *qp,
if (enforce_security) {
mutex_lock(&qp->qp_sec->mutex);
-
err = qp_modify_enforce_security(qp, qp_attr, qp_attr_mask);
}
@@ -311,6 +311,30 @@ static int read_port_immutable(struct ib_device *device)
return 0;
}
+static int setup_port_pkey_list(struct ib_device *device)
+{
+ int i;
+
+ /**
+ * device->port_pkey_list is indexed directly by the port number,
+ * Therefore it is declared as a 1 based array with potential empty
+ * slots at the beginning.
+ */
+ device->port_pkey_list = kzalloc(sizeof(*device->port_pkey_list)
+ * (rdma_end_port(device) + 1),
+ GFP_KERNEL);
+
+ if (!device->port_pkey_list)
+ return -ENOMEM;
+
+ for (i = 0; i < (rdma_end_port(device) + 1); i++) {
+ spin_lock_init(&device->port_pkey_list[i].list_lock);
+ INIT_LIST_HEAD(&device->port_pkey_list[i].pkey_list);
+ }
+
+ return 0;
+}
+
/**
* ib_register_device - Register an IB device with IB core
* @device:Device to register
@@ -348,6 +372,12 @@ int ib_register_device(struct ib_device *device,
goto out;
}
+ ret = setup_port_pkey_list(device);
+ if (ret) {
+ dev_warn(device->dma_device, "Couldn't create per port_pkey_list\n");
+ goto out;
+ }
+
ret = ib_cache_setup_one(device);
if (ret) {
pr_warn("Couldn't set up InfiniBand P_Key/GID cache\n");
@@ -418,6 +448,9 @@ void ib_unregister_device(struct ib_device *device)
ib_device_unregister_sysfs(device);
ib_cache_cleanup_one(device);
+ ib_security_destroy_port_pkey_list(device);
+ kfree(device->port_pkey_list);
+
down_write(&lists_rwsem);
spin_lock_irqsave(&device->client_data_lock, flags);
list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
@@ -1857,7 +1857,7 @@ static int create_qp(struct ib_uverbs_file *file,
}
if (cmd->qp_type != IB_QPT_XRC_TGT) {
- ret = ib_security_create_qp_security(qp);
+ ret = ib_security_create_qp_security(qp, device);
if (ret)
goto err_destroy;
@@ -688,13 +688,13 @@ static struct ib_qp *__ib_open_qp(struct ib_qp *real_qp,
if (!qp)
return ERR_PTR(-ENOMEM);
- qp->real_qp = real_qp;
- err = ib_security_open_shared_qp(qp);
+ err = ib_security_open_shared_qp(qp, real_qp->device);
if (err) {
kfree(qp);
return ERR_PTR(err);
}
+ qp->real_qp = real_qp;
atomic_inc(&real_qp->usecnt);
qp->device = real_qp->device;
qp->event_handler = event_handler;
@@ -742,7 +742,7 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
qp = device->create_qp(pd, qp_init_attr, NULL);
if (!IS_ERR(qp)) {
- err = ib_security_create_qp_security(qp);
+ err = ib_security_create_qp_security(qp, device);
if (err)
goto destroy_qp;
@@ -1280,6 +1280,8 @@ int ib_destroy_qp(struct ib_qp *qp)
rcq = qp->recv_cq;
srq = qp->srq;
sec = qp->qp_sec;
+ if (sec)
+ ib_security_destroy_qp_begin(sec);
ret = qp->device->destroy_qp(qp);
if (!ret) {
@@ -1292,7 +1294,10 @@ int ib_destroy_qp(struct ib_qp *qp)
if (srq)
atomic_dec(&srq->usecnt);
if (sec)
- ib_security_destroy_qp(sec);
+ ib_security_destroy_qp_end(sec);
+ } else {
+ if (sec)
+ ib_security_destroy_qp_abort(sec);
}
return ret;
@@ -1422,10 +1422,15 @@ enum port_pkey_state {
IB_PORT_PKEY_CHANGING = 2,
};
+struct ib_qp_security;
+
struct ib_port_pkey {
enum port_pkey_state state;
u16 pkey_index;
u8 port_num;
+ struct list_head qp_list;
+ struct list_head reset_list;
+ struct ib_qp_security *sec;
};
struct ib_ports_pkeys {
@@ -1435,6 +1440,7 @@ struct ib_ports_pkeys {
struct ib_qp_security {
struct ib_qp *qp;
+ struct ib_device *dev;
/* Hold this mutex when changing port and pkey settings. */
struct mutex mutex;
struct ib_ports_pkeys ports_pkeys;
@@ -1444,6 +1450,7 @@ struct ib_qp_security {
*/
struct list_head shared_qp_list;
void *q_security;
+ bool destroying;
};
struct ib_qp {
@@ -1693,6 +1700,12 @@ struct ib_port_immutable {
u32 max_mad_size;
};
+struct ib_port_pkey_list {
+ /* Lock to hold while modifying the list. */
+ spinlock_t list_lock;
+ struct list_head pkey_list;
+};
+
struct ib_device {
struct device *dma_device;
@@ -1715,6 +1728,8 @@ struct ib_device {
int num_comp_vectors;
+ struct ib_port_pkey_list *port_pkey_list;
+
struct iw_cm_verbs *iwcm;
int (*get_protocol_stats)(struct ib_device *device,