bpf, sockmap: Don't sleep while holding RCU lock on tear-down
[linux-2.6-microblaze.git] / net / core / sock_map.c
index 8998e35..9925044 100644 (file)
@@ -234,7 +234,6 @@ static void sock_map_free(struct bpf_map *map)
        int i;
 
        synchronize_rcu();
-       rcu_read_lock();
        raw_spin_lock_bh(&stab->lock);
        for (i = 0; i < stab->map.max_entries; i++) {
                struct sock **psk = &stab->sks[i];
@@ -243,12 +242,13 @@ static void sock_map_free(struct bpf_map *map)
                sk = xchg(psk, NULL);
                if (sk) {
                        lock_sock(sk);
+                       rcu_read_lock();
                        sock_map_unref(sk, psk);
+                       rcu_read_unlock();
                        release_sock(sk);
                }
        }
        raw_spin_unlock_bh(&stab->lock);
-       rcu_read_unlock();
 
        synchronize_rcu();
 
@@ -416,14 +416,16 @@ static int sock_map_update_elem(struct bpf_map *map, void *key,
                ret = -EINVAL;
                goto out;
        }
-       if (!sock_map_sk_is_suitable(sk) ||
-           sk->sk_state != TCP_ESTABLISHED) {
+       if (!sock_map_sk_is_suitable(sk)) {
                ret = -EOPNOTSUPP;
                goto out;
        }
 
        sock_map_sk_acquire(sk);
-       ret = sock_map_update_common(map, idx, sk, flags);
+       if (sk->sk_state != TCP_ESTABLISHED)
+               ret = -EOPNOTSUPP;
+       else
+               ret = sock_map_update_common(map, idx, sk, flags);
        sock_map_sk_release(sk);
 out:
        fput(sock->file);
@@ -739,14 +741,16 @@ static int sock_hash_update_elem(struct bpf_map *map, void *key,
                ret = -EINVAL;
                goto out;
        }
-       if (!sock_map_sk_is_suitable(sk) ||
-           sk->sk_state != TCP_ESTABLISHED) {
+       if (!sock_map_sk_is_suitable(sk)) {
                ret = -EOPNOTSUPP;
                goto out;
        }
 
        sock_map_sk_acquire(sk);
-       ret = sock_hash_update_common(map, key, sk, flags);
+       if (sk->sk_state != TCP_ESTABLISHED)
+               ret = -EOPNOTSUPP;
+       else
+               ret = sock_hash_update_common(map, key, sk, flags);
        sock_map_sk_release(sk);
 out:
        fput(sock->file);
@@ -859,19 +863,19 @@ static void sock_hash_free(struct bpf_map *map)
        int i;
 
        synchronize_rcu();
-       rcu_read_lock();
        for (i = 0; i < htab->buckets_num; i++) {
                bucket = sock_hash_select_bucket(htab, i);
                raw_spin_lock_bh(&bucket->lock);
                hlist_for_each_entry_safe(elem, node, &bucket->head, node) {
                        hlist_del_rcu(&elem->node);
                        lock_sock(elem->sk);
+                       rcu_read_lock();
                        sock_map_unref(elem->sk, elem);
+                       rcu_read_unlock();
                        release_sock(elem->sk);
                }
                raw_spin_unlock_bh(&bucket->lock);
        }
-       rcu_read_unlock();
 
        bpf_map_area_free(htab->buckets);
        kfree(htab);