diff options
Diffstat (limited to 'net/sctp/bind_addr.c')
| -rw-r--r-- | net/sctp/bind_addr.c | 70 |
1 files changed, 47 insertions, 23 deletions
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index fdb287a9e2e2..d35cbf5aae33 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c | |||
| @@ -163,9 +163,15 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, | |||
| 163 | addr->a.v4.sin_port = htons(bp->port); | 163 | addr->a.v4.sin_port = htons(bp->port); |
| 164 | 164 | ||
| 165 | addr->use_as_src = use_as_src; | 165 | addr->use_as_src = use_as_src; |
| 166 | addr->valid = 1; | ||
| 166 | 167 | ||
| 167 | INIT_LIST_HEAD(&addr->list); | 168 | INIT_LIST_HEAD(&addr->list); |
| 168 | list_add_tail(&addr->list, &bp->address_list); | 169 | INIT_RCU_HEAD(&addr->rcu); |
| 170 | |||
| 171 | /* We always hold a socket lock when calling this function, | ||
| 172 | * and that acts as a writer synchronizing lock. | ||
| 173 | */ | ||
| 174 | list_add_tail_rcu(&addr->list, &bp->address_list); | ||
| 169 | SCTP_DBG_OBJCNT_INC(addr); | 175 | SCTP_DBG_OBJCNT_INC(addr); |
| 170 | 176 | ||
| 171 | return 0; | 177 | return 0; |
| @@ -174,23 +180,35 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, | |||
| 174 | /* Delete an address from the bind address list in the SCTP_bind_addr | 180 | /* Delete an address from the bind address list in the SCTP_bind_addr |
| 175 | * structure. | 181 | * structure. |
| 176 | */ | 182 | */ |
| 177 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) | 183 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr, |
| 184 | void (*rcu_call)(struct rcu_head *head, | ||
| 185 | void (*func)(struct rcu_head *head))) | ||
| 178 | { | 186 | { |
| 179 | struct list_head *pos, *temp; | 187 | struct sctp_sockaddr_entry *addr, *temp; |
| 180 | struct sctp_sockaddr_entry *addr; | ||
| 181 | 188 | ||
| 182 | list_for_each_safe(pos, temp, &bp->address_list) { | 189 | /* We hold the socket lock when calling this function, |
| 183 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 190 | * and that acts as a writer synchronizing lock. |
| 191 | */ | ||
| 192 | list_for_each_entry_safe(addr, temp, &bp->address_list, list) { | ||
| 184 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { | 193 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { |
| 185 | /* Found the exact match. */ | 194 | /* Found the exact match. */ |
| 186 | list_del(pos); | 195 | addr->valid = 0; |
| 187 | kfree(addr); | 196 | list_del_rcu(&addr->list); |
| 188 | SCTP_DBG_OBJCNT_DEC(addr); | 197 | break; |
| 189 | |||
| 190 | return 0; | ||
| 191 | } | 198 | } |
| 192 | } | 199 | } |
| 193 | 200 | ||
| 201 | /* Call the rcu callback provided in the args. This function is | ||
| 202 | * called by both BH packet processing and user side socket option | ||
| 203 | * processing, but it works on different lists in those 2 contexts. | ||
| 204 | * Each context provides it's own callback, whether call_rcu_bh() | ||
| 205 | * or call_rcu(), to make sure that we wait for an appropriate time. | ||
| 206 | */ | ||
| 207 | if (addr && !addr->valid) { | ||
| 208 | rcu_call(&addr->rcu, sctp_local_addr_free); | ||
| 209 | SCTP_DBG_OBJCNT_DEC(addr); | ||
| 210 | } | ||
| 211 | |||
| 194 | return -EINVAL; | 212 | return -EINVAL; |
| 195 | } | 213 | } |
| 196 | 214 | ||
| @@ -300,15 +318,20 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp, | |||
| 300 | struct sctp_sock *opt) | 318 | struct sctp_sock *opt) |
| 301 | { | 319 | { |
| 302 | struct sctp_sockaddr_entry *laddr; | 320 | struct sctp_sockaddr_entry *laddr; |
| 303 | struct list_head *pos; | 321 | int match = 0; |
| 304 | 322 | ||
| 305 | list_for_each(pos, &bp->address_list) { | 323 | rcu_read_lock(); |
| 306 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 324 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 307 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | 325 | if (!laddr->valid) |
| 308 | return 1; | 326 | continue; |
| 327 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) { | ||
| 328 | match = 1; | ||
| 329 | break; | ||
| 330 | } | ||
| 309 | } | 331 | } |
| 332 | rcu_read_unlock(); | ||
| 310 | 333 | ||
| 311 | return 0; | 334 | return match; |
| 312 | } | 335 | } |
| 313 | 336 | ||
| 314 | /* Find the first address in the bind address list that is not present in | 337 | /* Find the first address in the bind address list that is not present in |
| @@ -323,18 +346,19 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, | |||
| 323 | union sctp_addr *addr; | 346 | union sctp_addr *addr; |
| 324 | void *addr_buf; | 347 | void *addr_buf; |
| 325 | struct sctp_af *af; | 348 | struct sctp_af *af; |
| 326 | struct list_head *pos; | ||
| 327 | int i; | 349 | int i; |
| 328 | 350 | ||
| 329 | list_for_each(pos, &bp->address_list) { | 351 | /* This is only called sctp_send_asconf_del_ip() and we hold |
| 330 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 352 | * the socket lock in that code patch, so that address list |
| 331 | 353 | * can't change. | |
| 354 | */ | ||
| 355 | list_for_each_entry(laddr, &bp->address_list, list) { | ||
| 332 | addr_buf = (union sctp_addr *)addrs; | 356 | addr_buf = (union sctp_addr *)addrs; |
| 333 | for (i = 0; i < addrcnt; i++) { | 357 | for (i = 0; i < addrcnt; i++) { |
| 334 | addr = (union sctp_addr *)addr_buf; | 358 | addr = (union sctp_addr *)addr_buf; |
| 335 | af = sctp_get_af_specific(addr->v4.sin_family); | 359 | af = sctp_get_af_specific(addr->v4.sin_family); |
| 336 | if (!af) | 360 | if (!af) |
| 337 | return NULL; | 361 | break; |
| 338 | 362 | ||
| 339 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | 363 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) |
| 340 | break; | 364 | break; |
