diff options
author | Leon Romanovsky <leonro@mellanox.com> | 2018-01-01 06:07:15 -0500 |
---|---|---|
committer | Jason Gunthorpe <jgg@mellanox.com> | 2018-01-02 16:11:40 -0500 |
commit | f8978bd95cf92f869f3d9b34c1b699f49253b8c6 (patch) | |
tree | 54b791e66ed3e93338982601d5f9eec8cad75e7d | |
parent | 16ba3defb8bd01a9464ba4820a487f5b196b455b (diff) |
RDMA/netlink: Fix locking around __ib_get_device_by_index
Holding locks is mandatory when calling __ib_device_get_by_index,
otherwise there are races during the list iteration with device removal.
Since the locks are static to device.c, __ib_device_get_by_index can
never be called correctly by any user out side the file.
Make the function static and provide a safe function that gets the
correct locks and returns a kref'd pointer. Fix all callers.
Fixes: e5c9469efcb1 ("RDMA/netlink: Add nldev device doit implementation")
Fixes: c3f66f7b0052 ("RDMA/netlink: Implement nldev port doit callback")
Fixes: 7d02f605f0dc ("RDMA/netlink: Add nldev port dumpit implementation")
Reviewed-by: Mark Bloch <markb@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
-rw-r--r-- | drivers/infiniband/core/core_priv.h | 2 | ||||
-rw-r--r-- | drivers/infiniband/core/device.c | 18 | ||||
-rw-r--r-- | drivers/infiniband/core/nldev.c | 54 |
3 files changed, 54 insertions, 20 deletions
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index a1d687a664f8..66f0268f37a6 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h | |||
@@ -314,7 +314,7 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map, | |||
314 | } | 314 | } |
315 | #endif | 315 | #endif |
316 | 316 | ||
317 | struct ib_device *__ib_device_get_by_index(u32 ifindex); | 317 | struct ib_device *ib_device_get_by_index(u32 ifindex); |
318 | /* RDMA device netlink */ | 318 | /* RDMA device netlink */ |
319 | void nldev_init(void); | 319 | void nldev_init(void); |
320 | void nldev_exit(void); | 320 | void nldev_exit(void); |
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 30914f3baa5f..465520627e4b 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c | |||
@@ -134,7 +134,7 @@ static int ib_device_check_mandatory(struct ib_device *device) | |||
134 | return 0; | 134 | return 0; |
135 | } | 135 | } |
136 | 136 | ||
137 | struct ib_device *__ib_device_get_by_index(u32 index) | 137 | static struct ib_device *__ib_device_get_by_index(u32 index) |
138 | { | 138 | { |
139 | struct ib_device *device; | 139 | struct ib_device *device; |
140 | 140 | ||
@@ -145,6 +145,22 @@ struct ib_device *__ib_device_get_by_index(u32 index) | |||
145 | return NULL; | 145 | return NULL; |
146 | } | 146 | } |
147 | 147 | ||
148 | /* | ||
149 | * Caller is responsible to return refrerence count by calling put_device() | ||
150 | */ | ||
151 | struct ib_device *ib_device_get_by_index(u32 index) | ||
152 | { | ||
153 | struct ib_device *device; | ||
154 | |||
155 | down_read(&lists_rwsem); | ||
156 | device = __ib_device_get_by_index(index); | ||
157 | if (device) | ||
158 | get_device(&device->dev); | ||
159 | |||
160 | up_read(&lists_rwsem); | ||
161 | return device; | ||
162 | } | ||
163 | |||
148 | static struct ib_device *__ib_device_get_by_name(const char *name) | 164 | static struct ib_device *__ib_device_get_by_name(const char *name) |
149 | { | 165 | { |
150 | struct ib_device *device; | 166 | struct ib_device *device; |
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 9a05245a1acf..0dcd1aa6f683 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c | |||
@@ -142,27 +142,34 @@ static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
142 | 142 | ||
143 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | 143 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
144 | 144 | ||
145 | device = __ib_device_get_by_index(index); | 145 | device = ib_device_get_by_index(index); |
146 | if (!device) | 146 | if (!device) |
147 | return -EINVAL; | 147 | return -EINVAL; |
148 | 148 | ||
149 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 149 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
150 | if (!msg) | 150 | if (!msg) { |
151 | return -ENOMEM; | 151 | err = -ENOMEM; |
152 | goto err; | ||
153 | } | ||
152 | 154 | ||
153 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | 155 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, |
154 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), | 156 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), |
155 | 0, 0); | 157 | 0, 0); |
156 | 158 | ||
157 | err = fill_dev_info(msg, device); | 159 | err = fill_dev_info(msg, device); |
158 | if (err) { | 160 | if (err) |
159 | nlmsg_free(msg); | 161 | goto err_free; |
160 | return err; | ||
161 | } | ||
162 | 162 | ||
163 | nlmsg_end(msg, nlh); | 163 | nlmsg_end(msg, nlh); |
164 | 164 | ||
165 | put_device(&device->dev); | ||
165 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | 166 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); |
167 | |||
168 | err_free: | ||
169 | nlmsg_free(msg); | ||
170 | err: | ||
171 | put_device(&device->dev); | ||
172 | return err; | ||
166 | } | 173 | } |
167 | 174 | ||
168 | static int _nldev_get_dumpit(struct ib_device *device, | 175 | static int _nldev_get_dumpit(struct ib_device *device, |
@@ -220,31 +227,40 @@ static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
220 | return -EINVAL; | 227 | return -EINVAL; |
221 | 228 | ||
222 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | 229 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
223 | device = __ib_device_get_by_index(index); | 230 | device = ib_device_get_by_index(index); |
224 | if (!device) | 231 | if (!device) |
225 | return -EINVAL; | 232 | return -EINVAL; |
226 | 233 | ||
227 | port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | 234 | port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); |
228 | if (!rdma_is_port_valid(device, port)) | 235 | if (!rdma_is_port_valid(device, port)) { |
229 | return -EINVAL; | 236 | err = -EINVAL; |
237 | goto err; | ||
238 | } | ||
230 | 239 | ||
231 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 240 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
232 | if (!msg) | 241 | if (!msg) { |
233 | return -ENOMEM; | 242 | err = -ENOMEM; |
243 | goto err; | ||
244 | } | ||
234 | 245 | ||
235 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | 246 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, |
236 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), | 247 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), |
237 | 0, 0); | 248 | 0, 0); |
238 | 249 | ||
239 | err = fill_port_info(msg, device, port); | 250 | err = fill_port_info(msg, device, port); |
240 | if (err) { | 251 | if (err) |
241 | nlmsg_free(msg); | 252 | goto err_free; |
242 | return err; | ||
243 | } | ||
244 | 253 | ||
245 | nlmsg_end(msg, nlh); | 254 | nlmsg_end(msg, nlh); |
255 | put_device(&device->dev); | ||
246 | 256 | ||
247 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | 257 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); |
258 | |||
259 | err_free: | ||
260 | nlmsg_free(msg); | ||
261 | err: | ||
262 | put_device(&device->dev); | ||
263 | return err; | ||
248 | } | 264 | } |
249 | 265 | ||
250 | static int nldev_port_get_dumpit(struct sk_buff *skb, | 266 | static int nldev_port_get_dumpit(struct sk_buff *skb, |
@@ -265,7 +281,7 @@ static int nldev_port_get_dumpit(struct sk_buff *skb, | |||
265 | return -EINVAL; | 281 | return -EINVAL; |
266 | 282 | ||
267 | ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | 283 | ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
268 | device = __ib_device_get_by_index(ifindex); | 284 | device = ib_device_get_by_index(ifindex); |
269 | if (!device) | 285 | if (!device) |
270 | return -EINVAL; | 286 | return -EINVAL; |
271 | 287 | ||
@@ -299,7 +315,9 @@ static int nldev_port_get_dumpit(struct sk_buff *skb, | |||
299 | nlmsg_end(skb, nlh); | 315 | nlmsg_end(skb, nlh); |
300 | } | 316 | } |
301 | 317 | ||
302 | out: cb->args[0] = idx; | 318 | out: |
319 | put_device(&device->dev); | ||
320 | cb->args[0] = idx; | ||
303 | return skb->len; | 321 | return skb->len; |
304 | } | 322 | } |
305 | 323 | ||