LCOV - code coverage report
Current view: top level - net/ipv4 - inet_timewait_sock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 79 173 45.7 %
Date: 2014-04-07 Functions: 9 14 64.3 %
Branches: 24 93 25.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * INET         An implementation of the TCP/IP protocol suite for the LINUX
       3                 :            :  *              operating system.  INET is implemented using the  BSD Socket
       4                 :            :  *              interface as the means of communication with the user level.
       5                 :            :  *
       6                 :            :  *              Generic TIME_WAIT sockets functions
       7                 :            :  *
       8                 :            :  *              From code orinally in TCP
       9                 :            :  */
      10                 :            : 
      11                 :            : #include <linux/kernel.h>
      12                 :            : #include <linux/kmemcheck.h>
      13                 :            : #include <linux/slab.h>
      14                 :            : #include <linux/module.h>
      15                 :            : #include <net/inet_hashtables.h>
      16                 :            : #include <net/inet_timewait_sock.h>
      17                 :            : #include <net/ip.h>
      18                 :            : 
      19                 :            : 
      20                 :            : /**
      21                 :            :  *      inet_twsk_unhash - unhash a timewait socket from established hash
      22                 :            :  *      @tw: timewait socket
      23                 :            :  *
      24                 :            :  *      unhash a timewait socket from established hash, if hashed.
      25                 :            :  *      ehash lock must be held by caller.
      26                 :            :  *      Returns 1 if caller should call inet_twsk_put() after lock release.
      27                 :            :  */
      28                 :          0 : int inet_twsk_unhash(struct inet_timewait_sock *tw)
      29                 :            : {
      30 [ +  - ][ #  # ]:         16 :         if (hlist_nulls_unhashed(&tw->tw_node))
      31                 :            :                 return 0;
      32                 :            : 
      33                 :            :         hlist_nulls_del_rcu(&tw->tw_node);
      34                 :            :         sk_nulls_node_init(&tw->tw_node);
      35                 :            :         /*
      36                 :            :          * We cannot call inet_twsk_put() ourself under lock,
      37                 :            :          * caller must call it for us.
      38                 :            :          */
      39                 :          0 :         return 1;
      40                 :            : }
      41                 :            : 
      42                 :            : /**
      43                 :            :  *      inet_twsk_bind_unhash - unhash a timewait socket from bind hash
      44                 :            :  *      @tw: timewait socket
      45                 :            :  *      @hashinfo: hashinfo pointer
      46                 :            :  *
      47                 :            :  *      unhash a timewait socket from bind hash, if hashed.
      48                 :            :  *      bind hash lock must be held by caller.
      49                 :            :  *      Returns 1 if caller should call inet_twsk_put() after lock release.
      50                 :            :  */
      51                 :          0 : int inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
      52                 :            :                           struct inet_hashinfo *hashinfo)
      53                 :            : {
      54                 :         16 :         struct inet_bind_bucket *tb = tw->tw_tb;
      55                 :            : 
      56         [ +  - ]:         16 :         if (!tb)
      57                 :            :                 return 0;
      58                 :            : 
      59                 :            :         __hlist_del(&tw->tw_bind_node);
      60                 :         16 :         tw->tw_tb = NULL;
      61                 :         16 :         inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb);
      62                 :            :         /*
      63                 :            :          * We cannot call inet_twsk_put() ourself under lock,
      64                 :            :          * caller must call it for us.
      65                 :            :          */
      66                 :         16 :         return 1;
      67                 :            : }
      68                 :            : 
      69                 :            : /* Must be called with locally disabled BHs. */
      70                 :          0 : static void __inet_twsk_kill(struct inet_timewait_sock *tw,
      71                 :         16 :                              struct inet_hashinfo *hashinfo)
      72                 :            : {
      73                 :            :         struct inet_bind_hashbucket *bhead;
      74                 :            :         int refcnt;
      75                 :            :         /* Unlink from established hashes. */
      76                 :         16 :         spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash);
      77                 :            : 
      78                 :            :         spin_lock(lock);
      79                 :            :         refcnt = inet_twsk_unhash(tw);
      80                 :            :         spin_unlock(lock);
      81                 :            : 
      82                 :            :         /* Disassociate with bind bucket. */
      83                 :         48 :         bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), tw->tw_num,
      84                 :         16 :                         hashinfo->bhash_size)];
      85                 :            : 
      86                 :            :         spin_lock(&bhead->lock);
      87                 :         16 :         refcnt += inet_twsk_bind_unhash(tw, hashinfo);
      88                 :            :         spin_unlock(&bhead->lock);
      89                 :            : 
      90         [ -  + ]:         16 :         BUG_ON(refcnt >= atomic_read(&tw->tw_refcnt));
      91                 :         16 :         atomic_sub(refcnt, &tw->tw_refcnt);
      92                 :         16 : }
      93                 :            : 
      94                 :          0 : void inet_twsk_free(struct inet_timewait_sock *tw)
      95                 :            : {
      96                 :         16 :         struct module *owner = tw->tw_prot->owner;
      97                 :            :         twsk_destructor((struct sock *)tw);
      98                 :            : #ifdef SOCK_REFCNT_DEBUG
      99                 :            :         pr_debug("%s timewait_sock %p released\n", tw->tw_prot->name, tw);
     100                 :            : #endif
     101                 :            :         release_net(twsk_net(tw));
     102                 :         16 :         kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw);
     103                 :         16 :         module_put(owner);
     104                 :         16 : }
     105                 :            : 
     106                 :          0 : void inet_twsk_put(struct inet_timewait_sock *tw)
     107                 :            : {
     108         [ +  + ]:         32 :         if (atomic_dec_and_test(&tw->tw_refcnt))
     109                 :         16 :                 inet_twsk_free(tw);
     110                 :          0 : }
     111                 :            : EXPORT_SYMBOL_GPL(inet_twsk_put);
     112                 :            : 
     113                 :            : static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
     114                 :            :                                    struct hlist_nulls_head *list)
     115                 :            : {
     116                 :         16 :         hlist_nulls_add_head_rcu(&tw->tw_node, list);
     117                 :            : }
     118                 :            : 
     119                 :            : static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
     120                 :            :                                     struct hlist_head *list)
     121                 :            : {
     122                 :         16 :         hlist_add_head(&tw->tw_bind_node, list);
     123                 :            : }
     124                 :            : 
     125                 :            : /*
     126                 :            :  * Enter the time wait state. This is called with locally disabled BH.
     127                 :            :  * Essentially we whip up a timewait bucket, copy the relevant info into it
     128                 :            :  * from the SK, and mess with hash chains and list linkage.
     129                 :            :  */
     130                 :          0 : void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
     131                 :         32 :                            struct inet_hashinfo *hashinfo)
     132                 :            : {
     133                 :            :         const struct inet_sock *inet = inet_sk(sk);
     134                 :            :         const struct inet_connection_sock *icsk = inet_csk(sk);
     135                 :         16 :         struct inet_ehash_bucket *ehead = inet_ehash_bucket(hashinfo, sk->sk_hash);
     136                 :            :         spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
     137                 :            :         struct inet_bind_hashbucket *bhead;
     138                 :            :         /* Step 1: Put TW into bind hash. Original socket stays there too.
     139                 :            :            Note, that any socket with inet->num != 0 MUST be bound in
     140                 :            :            binding cache, even if it is closed.
     141                 :            :          */
     142                 :         48 :         bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), inet->inet_num,
     143                 :         16 :                         hashinfo->bhash_size)];
     144                 :            :         spin_lock(&bhead->lock);
     145                 :         16 :         tw->tw_tb = icsk->icsk_bind_hash;
     146         [ -  + ]:         16 :         WARN_ON(!icsk->icsk_bind_hash);
     147                 :         16 :         inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
     148                 :            :         spin_unlock(&bhead->lock);
     149                 :            : 
     150                 :            :         spin_lock(lock);
     151                 :            : 
     152                 :            :         /*
     153                 :            :          * Step 2: Hash TW into tcp ehash chain.
     154                 :            :          * Notes :
     155                 :            :          * - tw_refcnt is set to 3 because :
     156                 :            :          * - We have one reference from bhash chain.
     157                 :            :          * - We have one reference from ehash chain.
     158                 :            :          * We can use atomic_set() because prior spin_lock()/spin_unlock()
     159                 :            :          * committed into memory all tw fields.
     160                 :            :          */
     161                 :         16 :         atomic_set(&tw->tw_refcnt, 1 + 1 + 1);
     162                 :            :         inet_twsk_add_node_rcu(tw, &ehead->chain);
     163                 :            : 
     164                 :            :         /* Step 3: Remove SK from hash chain */
     165            [ + ]:         16 :         if (__sk_nulls_del_node_init_rcu(sk))
     166                 :         16 :                 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
     167                 :            : 
     168                 :            :         spin_unlock(lock);
     169                 :         16 : }
     170                 :            : EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
     171                 :            : 
     172                 :          0 : struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
     173                 :            : {
     174                 :         16 :         struct inet_timewait_sock *tw =
     175                 :         16 :                 kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
     176                 :            :                                  GFP_ATOMIC);
     177         [ +  - ]:         16 :         if (tw != NULL) {
     178                 :            :                 const struct inet_sock *inet = inet_sk(sk);
     179                 :            : 
     180                 :            :                 kmemcheck_annotate_bitfield(tw, flags);
     181                 :            : 
     182                 :            :                 /* Give us an identity. */
     183                 :         16 :                 tw->tw_daddr     = inet->inet_daddr;
     184                 :         16 :                 tw->tw_rcv_saddr    = inet->inet_rcv_saddr;
     185                 :         16 :                 tw->tw_bound_dev_if = sk->sk_bound_dev_if;
     186                 :         16 :                 tw->tw_tos       = inet->tos;
     187                 :         16 :                 tw->tw_num       = inet->inet_num;
     188                 :         16 :                 tw->tw_state     = TCP_TIME_WAIT;
     189                 :         16 :                 tw->tw_substate          = state;
     190                 :         16 :                 tw->tw_sport     = inet->inet_sport;
     191                 :         16 :                 tw->tw_dport     = inet->inet_dport;
     192                 :         16 :                 tw->tw_family            = sk->sk_family;
     193                 :         16 :                 tw->tw_reuse     = sk->sk_reuse;
     194                 :         16 :                 tw->tw_hash      = sk->sk_hash;
     195                 :         16 :                 tw->tw_ipv6only          = 0;
     196                 :         16 :                 tw->tw_transparent  = inet->transparent;
     197                 :         16 :                 tw->tw_prot      = sk->sk_prot_creator;
     198                 :            :                 twsk_net_set(tw, hold_net(sock_net(sk)));
     199                 :            :                 /*
     200                 :            :                  * Because we use RCU lookups, we should not set tw_refcnt
     201                 :            :                  * to a non null value before everything is setup for this
     202                 :            :                  * timewait socket.
     203                 :            :                  */
     204                 :         16 :                 atomic_set(&tw->tw_refcnt, 0);
     205                 :            :                 inet_twsk_dead_node_init(tw);
     206                 :         16 :                 __module_get(tw->tw_prot->owner);
     207                 :            :         }
     208                 :            : 
     209                 :          0 :         return tw;
     210                 :            : }
     211                 :            : EXPORT_SYMBOL_GPL(inet_twsk_alloc);
     212                 :            : 
     213                 :            : /* Returns non-zero if quota exceeded.  */
     214                 :         49 : static int inet_twdr_do_twkill_work(struct inet_timewait_death_row *twdr,
     215                 :            :                                     const int slot)
     216                 :            : {
     217                 :            :         struct inet_timewait_sock *tw;
     218                 :            :         unsigned int killed;
     219                 :            :         int ret;
     220                 :            : 
     221                 :            :         /* NOTE: compare this to previous version where lock
     222                 :            :          * was released after detaching chain. It was racy,
     223                 :            :          * because tw buckets are scheduled in not serialized context
     224                 :            :          * in 2.3 (with netfilter), and with softnet it is common, because
     225                 :            :          * soft irqs are not sequenced.
     226                 :            :          */
     227                 :            :         killed = 0;
     228                 :            :         ret = 0;
     229                 :            : rescan:
     230 [ +  + ][ +  + ]:         65 :         inet_twsk_for_each_inmate(tw, &twdr->cells[slot]) {
     231                 :            :                 __inet_twsk_del_dead_node(tw);
     232                 :            :                 spin_unlock(&twdr->death_lock);
     233                 :         16 :                 __inet_twsk_kill(tw, twdr->hashinfo);
     234                 :            : #ifdef CONFIG_NET_NS
     235                 :            :                 NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITED);
     236                 :            : #endif
     237                 :         16 :                 inet_twsk_put(tw);
     238                 :         16 :                 killed++;
     239                 :            :                 spin_lock(&twdr->death_lock);
     240         [ +  - ]:         16 :                 if (killed > INET_TWDR_TWKILL_QUOTA) {
     241                 :            :                         ret = 1;
     242                 :            :                         break;
     243                 :            :                 }
     244                 :            : 
     245                 :            :                 /* While we dropped twdr->death_lock, another cpu may have
     246                 :            :                  * killed off the next TW bucket in the list, therefore
     247                 :            :                  * do a fresh re-read of the hlist head node with the
     248                 :            :                  * lock reacquired.  We still use the hlist traversal
     249                 :            :                  * macro in order to get the prefetches.
     250                 :            :                  */
     251                 :            :                 goto rescan;
     252                 :            :         }
     253                 :            : 
     254                 :          0 :         twdr->tw_count -= killed;
     255                 :            : #ifndef CONFIG_NET_NS
     256                 :          0 :         NET_ADD_STATS_BH(&init_net, LINUX_MIB_TIMEWAITED, killed);
     257                 :            : #endif
     258                 :          0 :         return ret;
     259                 :            : }
     260                 :            : 
     261                 :          0 : void inet_twdr_hangman(unsigned long data)
     262                 :            : {
     263                 :            :         struct inet_timewait_death_row *twdr;
     264                 :            :         unsigned int need_timer;
     265                 :            : 
     266                 :         49 :         twdr = (struct inet_timewait_death_row *)data;
     267                 :            :         spin_lock(&twdr->death_lock);
     268                 :            : 
     269         [ +  - ]:         49 :         if (twdr->tw_count == 0)
     270                 :            :                 goto out;
     271                 :            : 
     272                 :            :         need_timer = 0;
     273         [ -  + ]:         49 :         if (inet_twdr_do_twkill_work(twdr, twdr->slot)) {
     274                 :          0 :                 twdr->thread_slots |= (1 << twdr->slot);
     275                 :          0 :                 schedule_work(&twdr->twkill_work);
     276                 :            :                 need_timer = 1;
     277                 :            :         } else {
     278                 :            :                 /* We purged the entire slot, anything left?  */
     279         [ +  + ]:         49 :                 if (twdr->tw_count)
     280                 :            :                         need_timer = 1;
     281                 :         49 :                 twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));
     282                 :            :         }
     283         [ +  + ]:         49 :         if (need_timer)
     284                 :         44 :                 mod_timer(&twdr->tw_timer, jiffies + twdr->period);
     285                 :            : out:
     286                 :            :         spin_unlock(&twdr->death_lock);
     287                 :         49 : }
     288                 :            : EXPORT_SYMBOL_GPL(inet_twdr_hangman);
     289                 :            : 
     290                 :          0 : void inet_twdr_twkill_work(struct work_struct *work)
     291                 :            : {
     292                 :          0 :         struct inet_timewait_death_row *twdr =
     293                 :            :                 container_of(work, struct inet_timewait_death_row, twkill_work);
     294                 :            :         int i;
     295                 :            : 
     296                 :            :         BUILD_BUG_ON((INET_TWDR_TWKILL_SLOTS - 1) >
     297                 :            :                         (sizeof(twdr->thread_slots) * 8));
     298                 :            : 
     299         [ #  # ]:          0 :         while (twdr->thread_slots) {
     300                 :            :                 spin_lock_bh(&twdr->death_lock);
     301         [ #  # ]:          0 :                 for (i = 0; i < INET_TWDR_TWKILL_SLOTS; i++) {
     302         [ #  # ]:          0 :                         if (!(twdr->thread_slots & (1 << i)))
     303                 :          0 :                                 continue;
     304                 :            : 
     305         [ #  # ]:          0 :                         while (inet_twdr_do_twkill_work(twdr, i) != 0) {
     306         [ #  # ]:          0 :                                 if (need_resched()) {
     307                 :            :                                         spin_unlock_bh(&twdr->death_lock);
     308                 :          0 :                                         schedule();
     309                 :            :                                         spin_lock_bh(&twdr->death_lock);
     310                 :            :                                 }
     311                 :            :                         }
     312                 :            : 
     313                 :          0 :                         twdr->thread_slots &= ~(1 << i);
     314                 :            :                 }
     315                 :            :                 spin_unlock_bh(&twdr->death_lock);
     316                 :            :         }
     317                 :          0 : }
     318                 :            : EXPORT_SYMBOL_GPL(inet_twdr_twkill_work);
     319                 :            : 
     320                 :            : /* These are always called from BH context.  See callers in
     321                 :            :  * tcp_input.c to verify this.
     322                 :            :  */
     323                 :            : 
     324                 :            : /* This is for handling early-kills of TIME_WAIT sockets. */
     325                 :          0 : void inet_twsk_deschedule(struct inet_timewait_sock *tw,
     326                 :            :                           struct inet_timewait_death_row *twdr)
     327                 :            : {
     328                 :            :         spin_lock(&twdr->death_lock);
     329         [ #  # ]:          0 :         if (inet_twsk_del_dead_node(tw)) {
     330                 :          0 :                 inet_twsk_put(tw);
     331         [ #  # ]:          0 :                 if (--twdr->tw_count == 0)
     332                 :          0 :                         del_timer(&twdr->tw_timer);
     333                 :            :         }
     334                 :            :         spin_unlock(&twdr->death_lock);
     335                 :          0 :         __inet_twsk_kill(tw, twdr->hashinfo);
     336                 :          0 : }
     337                 :            : EXPORT_SYMBOL(inet_twsk_deschedule);
     338                 :            : 
     339                 :          0 : void inet_twsk_schedule(struct inet_timewait_sock *tw,
     340                 :            :                        struct inet_timewait_death_row *twdr,
     341                 :            :                        const int timeo, const int timewait_len)
     342                 :            : {
     343                 :            :         struct hlist_head *list;
     344                 :            :         int slot;
     345                 :            : 
     346                 :            :         /* timeout := RTO * 3.5
     347                 :            :          *
     348                 :            :          * 3.5 = 1+2+0.5 to wait for two retransmits.
     349                 :            :          *
     350                 :            :          * RATIONALE: if FIN arrived and we entered TIME-WAIT state,
     351                 :            :          * our ACK acking that FIN can be lost. If N subsequent retransmitted
     352                 :            :          * FINs (or previous seqments) are lost (probability of such event
     353                 :            :          * is p^(N+1), where p is probability to lose single packet and
     354                 :            :          * time to detect the loss is about RTO*(2^N - 1) with exponential
     355                 :            :          * backoff). Normal timewait length is calculated so, that we
     356                 :            :          * waited at least for one retransmitted FIN (maximal RTO is 120sec).
     357                 :            :          * [ BTW Linux. following BSD, violates this requirement waiting
     358                 :            :          *   only for 60sec, we should wait at least for 240 secs.
     359                 :            :          *   Well, 240 consumes too much of resources 8)
     360                 :            :          * ]
     361                 :            :          * This interval is not reduced to catch old duplicate and
     362                 :            :          * responces to our wandering segments living for two MSLs.
     363                 :            :          * However, if we use PAWS to detect
     364                 :            :          * old duplicates, we can reduce the interval to bounds required
     365                 :            :          * by RTO, rather than MSL. So, if peer understands PAWS, we
     366                 :            :          * kill tw bucket after 3.5*RTO (it is important that this number
     367                 :            :          * is greater than TS tick!) and detect old duplicates with help
     368                 :            :          * of PAWS.
     369                 :            :          */
     370                 :         16 :         slot = (timeo + (1 << INET_TWDR_RECYCLE_TICK) - 1) >> INET_TWDR_RECYCLE_TICK;
     371                 :            : 
     372                 :            :         spin_lock(&twdr->death_lock);
     373                 :            : 
     374                 :            :         /* Unlink it, if it was scheduled */
     375         [ -  + ]:         16 :         if (inet_twsk_del_dead_node(tw))
     376                 :          0 :                 twdr->tw_count--;
     377                 :            :         else
     378                 :         16 :                 atomic_inc(&tw->tw_refcnt);
     379                 :            : 
     380         [ +  - ]:         16 :         if (slot >= INET_TWDR_RECYCLE_SLOTS) {
     381                 :            :                 /* Schedule to slow timer */
     382         [ -  + ]:         16 :                 if (timeo >= timewait_len) {
     383                 :            :                         slot = INET_TWDR_TWKILL_SLOTS - 1;
     384                 :            :                 } else {
     385                 :          0 :                         slot = DIV_ROUND_UP(timeo, twdr->period);
     386         [ #  # ]:          0 :                         if (slot >= INET_TWDR_TWKILL_SLOTS)
     387                 :            :                                 slot = INET_TWDR_TWKILL_SLOTS - 1;
     388                 :            :                 }
     389                 :         16 :                 tw->tw_ttd = inet_tw_time_stamp() + timeo;
     390                 :         16 :                 slot = (twdr->slot + slot) & (INET_TWDR_TWKILL_SLOTS - 1);
     391                 :         16 :                 list = &twdr->cells[slot];
     392                 :            :         } else {
     393                 :          0 :                 tw->tw_ttd = inet_tw_time_stamp() + (slot << INET_TWDR_RECYCLE_TICK);
     394                 :            : 
     395         [ #  # ]:          0 :                 if (twdr->twcal_hand < 0) {
     396                 :          0 :                         twdr->twcal_hand = 0;
     397                 :          0 :                         twdr->twcal_jiffie = jiffies;
     398                 :          0 :                         twdr->twcal_timer.expires = twdr->twcal_jiffie +
     399                 :            :                                               (slot << INET_TWDR_RECYCLE_TICK);
     400                 :          0 :                         add_timer(&twdr->twcal_timer);
     401                 :            :                 } else {
     402         [ #  # ]:          0 :                         if (time_after(twdr->twcal_timer.expires,
     403                 :            :                                        jiffies + (slot << INET_TWDR_RECYCLE_TICK)))
     404                 :          0 :                                 mod_timer(&twdr->twcal_timer,
     405                 :            :                                           jiffies + (slot << INET_TWDR_RECYCLE_TICK));
     406                 :          0 :                         slot = (twdr->twcal_hand + slot) & (INET_TWDR_RECYCLE_SLOTS - 1);
     407                 :            :                 }
     408                 :          0 :                 list = &twdr->twcal_row[slot];
     409                 :            :         }
     410                 :            : 
     411                 :         16 :         hlist_add_head(&tw->tw_death_node, list);
     412                 :            : 
     413         [ +  + ]:         16 :         if (twdr->tw_count++ == 0)
     414                 :          5 :                 mod_timer(&twdr->tw_timer, jiffies + twdr->period);
     415                 :            :         spin_unlock(&twdr->death_lock);
     416                 :         16 : }
     417                 :            : EXPORT_SYMBOL_GPL(inet_twsk_schedule);
     418                 :            : 
     419                 :          0 : void inet_twdr_twcal_tick(unsigned long data)
     420                 :            : {
     421                 :            :         struct inet_timewait_death_row *twdr;
     422                 :            :         int n, slot;
     423                 :            :         unsigned long j;
     424                 :          0 :         unsigned long now = jiffies;
     425                 :            :         int killed = 0;
     426                 :            :         int adv = 0;
     427                 :            : 
     428                 :          0 :         twdr = (struct inet_timewait_death_row *)data;
     429                 :            : 
     430                 :            :         spin_lock(&twdr->death_lock);
     431         [ #  # ]:          0 :         if (twdr->twcal_hand < 0)
     432                 :            :                 goto out;
     433                 :            : 
     434                 :            :         slot = twdr->twcal_hand;
     435                 :          0 :         j = twdr->twcal_jiffie;
     436                 :            : 
     437         [ #  # ]:          0 :         for (n = 0; n < INET_TWDR_RECYCLE_SLOTS; n++) {
     438         [ #  # ]:          0 :                 if (time_before_eq(j, now)) {
     439                 :            :                         struct hlist_node *safe;
     440                 :            :                         struct inet_timewait_sock *tw;
     441                 :            : 
     442 [ #  # ][ #  # ]:          0 :                         inet_twsk_for_each_inmate_safe(tw, safe,
                 [ #  # ]
     443                 :            :                                                        &twdr->twcal_row[slot]) {
     444                 :            :                                 __inet_twsk_del_dead_node(tw);
     445                 :          0 :                                 __inet_twsk_kill(tw, twdr->hashinfo);
     446                 :            : #ifdef CONFIG_NET_NS
     447                 :            :                                 NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED);
     448                 :            : #endif
     449                 :          0 :                                 inet_twsk_put(tw);
     450                 :          0 :                                 killed++;
     451                 :            :                         }
     452                 :            :                 } else {
     453         [ #  # ]:          0 :                         if (!adv) {
     454                 :            :                                 adv = 1;
     455                 :          0 :                                 twdr->twcal_jiffie = j;
     456                 :          0 :                                 twdr->twcal_hand = slot;
     457                 :            :                         }
     458                 :            : 
     459         [ #  # ]:          0 :                         if (!hlist_empty(&twdr->twcal_row[slot])) {
     460                 :          0 :                                 mod_timer(&twdr->twcal_timer, j);
     461                 :          0 :                                 goto out;
     462                 :            :                         }
     463                 :            :                 }
     464                 :          0 :                 j += 1 << INET_TWDR_RECYCLE_TICK;
     465                 :          0 :                 slot = (slot + 1) & (INET_TWDR_RECYCLE_SLOTS - 1);
     466                 :            :         }
     467                 :          0 :         twdr->twcal_hand = -1;
     468                 :            : 
     469                 :            : out:
     470         [ #  # ]:          0 :         if ((twdr->tw_count -= killed) == 0)
     471                 :          0 :                 del_timer(&twdr->tw_timer);
     472                 :            : #ifndef CONFIG_NET_NS
     473                 :          0 :         NET_ADD_STATS_BH(&init_net, LINUX_MIB_TIMEWAITKILLED, killed);
     474                 :            : #endif
     475                 :            :         spin_unlock(&twdr->death_lock);
     476                 :          0 : }
     477                 :            : EXPORT_SYMBOL_GPL(inet_twdr_twcal_tick);
     478                 :            : 
     479                 :          0 : void inet_twsk_purge(struct inet_hashinfo *hashinfo,
     480                 :            :                      struct inet_timewait_death_row *twdr, int family)
     481                 :            : {
     482                 :            :         struct inet_timewait_sock *tw;
     483                 :            :         struct sock *sk;
     484                 :            :         struct hlist_nulls_node *node;
     485                 :            :         unsigned int slot;
     486                 :            : 
     487         [ #  # ]:          0 :         for (slot = 0; slot <= hashinfo->ehash_mask; slot++) {
     488                 :          0 :                 struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
     489                 :            : restart_rcu:
     490                 :            :                 rcu_read_lock();
     491                 :            : restart:
     492         [ #  # ]:          0 :                 sk_nulls_for_each_rcu(sk, node, &head->chain) {
     493         [ #  # ]:          0 :                         if (sk->sk_state != TCP_TIME_WAIT)
     494                 :          0 :                                 continue;
     495                 :          0 :                         tw = inet_twsk(sk);
     496 [ #  # ][ #  # ]:          0 :                         if ((tw->tw_family != family) ||
     497                 :          0 :                                 atomic_read(&twsk_net(tw)->count))
     498                 :          0 :                                 continue;
     499                 :            : 
     500         [ #  # ]:          0 :                         if (unlikely(!atomic_inc_not_zero(&tw->tw_refcnt)))
     501                 :          0 :                                 continue;
     502                 :            : 
     503 [ #  # ][ #  # ]:          0 :                         if (unlikely((tw->tw_family != family) ||
     504                 :            :                                      atomic_read(&twsk_net(tw)->count))) {
     505                 :          0 :                                 inet_twsk_put(tw);
     506                 :          0 :                                 goto restart;
     507                 :            :                         }
     508                 :            : 
     509                 :            :                         rcu_read_unlock();
     510                 :          0 :                         local_bh_disable();
     511                 :          0 :                         inet_twsk_deschedule(tw, twdr);
     512                 :          0 :                         local_bh_enable();
     513                 :          0 :                         inet_twsk_put(tw);
     514                 :          0 :                         goto restart_rcu;
     515                 :            :                 }
     516                 :            :                 /* If the nulls value we got at the end of this lookup is
     517                 :            :                  * not the expected one, we must restart lookup.
     518                 :            :                  * We probably met an item that was moved to another chain.
     519                 :            :                  */
     520         [ #  # ]:          0 :                 if (get_nulls_value(node) != slot)
     521                 :            :                         goto restart;
     522                 :            :                 rcu_read_unlock();
     523                 :            :         }
     524                 :          0 : }
     525                 :            : EXPORT_SYMBOL_GPL(inet_twsk_purge);

Generated by: LCOV version 1.9