LCOV - code coverage report
Current view: top level - net/ipv4 - tcp_ulp.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 3 68 4.4 %
Date: 2021-04-22 12:43:58 Functions: 1 9 11.1 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * Pluggable TCP upper layer protocol support.
       4             :  *
       5             :  * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
       6             :  * Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
       7             :  *
       8             :  */
       9             : 
      10             : #include <linux/module.h>
      11             : #include <linux/mm.h>
      12             : #include <linux/types.h>
      13             : #include <linux/list.h>
      14             : #include <linux/gfp.h>
      15             : #include <net/tcp.h>
      16             : 
      17             : static DEFINE_SPINLOCK(tcp_ulp_list_lock);
      18             : static LIST_HEAD(tcp_ulp_list);
      19             : 
      20             : /* Simple linear search, don't expect many entries! */
      21           0 : static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
      22             : {
      23           0 :         struct tcp_ulp_ops *e;
      24             : 
      25           0 :         list_for_each_entry_rcu(e, &tcp_ulp_list, list,
      26             :                                 lockdep_is_held(&tcp_ulp_list_lock)) {
      27           0 :                 if (strcmp(e->name, name) == 0)
      28           0 :                         return e;
      29             :         }
      30             : 
      31             :         return NULL;
      32             : }
      33             : 
      34           0 : static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
      35             : {
      36           0 :         const struct tcp_ulp_ops *ulp = NULL;
      37             : 
      38           0 :         rcu_read_lock();
      39           0 :         ulp = tcp_ulp_find(name);
      40             : 
      41             : #ifdef CONFIG_MODULES
      42             :         if (!ulp && capable(CAP_NET_ADMIN)) {
      43             :                 rcu_read_unlock();
      44             :                 request_module("tcp-ulp-%s", name);
      45             :                 rcu_read_lock();
      46             :                 ulp = tcp_ulp_find(name);
      47             :         }
      48             : #endif
      49           0 :         if (!ulp || !try_module_get(ulp->owner))
      50             :                 ulp = NULL;
      51             : 
      52           0 :         rcu_read_unlock();
      53           0 :         return ulp;
      54             : }
      55             : 
      56             : /* Attach new upper layer protocol to the list
      57             :  * of available protocols.
      58             :  */
      59           0 : int tcp_register_ulp(struct tcp_ulp_ops *ulp)
      60             : {
      61           0 :         int ret = 0;
      62             : 
      63           0 :         spin_lock(&tcp_ulp_list_lock);
      64           0 :         if (tcp_ulp_find(ulp->name))
      65             :                 ret = -EEXIST;
      66             :         else
      67           0 :                 list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
      68           0 :         spin_unlock(&tcp_ulp_list_lock);
      69             : 
      70           0 :         return ret;
      71             : }
      72             : EXPORT_SYMBOL_GPL(tcp_register_ulp);
      73             : 
      74           0 : void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
      75             : {
      76           0 :         spin_lock(&tcp_ulp_list_lock);
      77           0 :         list_del_rcu(&ulp->list);
      78           0 :         spin_unlock(&tcp_ulp_list_lock);
      79             : 
      80           0 :         synchronize_rcu();
      81           0 : }
      82             : EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
      83             : 
      84             : /* Build string with list of available upper layer protocl values */
      85           0 : void tcp_get_available_ulp(char *buf, size_t maxlen)
      86             : {
      87           0 :         struct tcp_ulp_ops *ulp_ops;
      88           0 :         size_t offs = 0;
      89             : 
      90           0 :         *buf = '\0';
      91           0 :         rcu_read_lock();
      92           0 :         list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
      93           0 :                 offs += snprintf(buf + offs, maxlen - offs,
      94             :                                  "%s%s",
      95           0 :                                  offs == 0 ? "" : " ", ulp_ops->name);
      96             : 
      97           0 :                 if (WARN_ON_ONCE(offs >= maxlen))
      98             :                         break;
      99             :         }
     100           0 :         rcu_read_unlock();
     101           0 : }
     102             : 
     103           0 : void tcp_update_ulp(struct sock *sk, struct proto *proto,
     104             :                     void (*write_space)(struct sock *sk))
     105             : {
     106           0 :         struct inet_connection_sock *icsk = inet_csk(sk);
     107             : 
     108           0 :         if (icsk->icsk_ulp_ops->update)
     109           0 :                 icsk->icsk_ulp_ops->update(sk, proto, write_space);
     110           0 : }
     111             : 
     112           4 : void tcp_cleanup_ulp(struct sock *sk)
     113             : {
     114           4 :         struct inet_connection_sock *icsk = inet_csk(sk);
     115             : 
     116             :         /* No sock_owned_by_me() check here as at the time the
     117             :          * stack calls this function, the socket is dead and
     118             :          * about to be destroyed.
     119             :          */
     120           4 :         if (!icsk->icsk_ulp_ops)
     121             :                 return;
     122             : 
     123           0 :         if (icsk->icsk_ulp_ops->release)
     124           0 :                 icsk->icsk_ulp_ops->release(sk);
     125           0 :         module_put(icsk->icsk_ulp_ops->owner);
     126             : 
     127           0 :         icsk->icsk_ulp_ops = NULL;
     128             : }
     129             : 
     130           0 : static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
     131             : {
     132           0 :         struct inet_connection_sock *icsk = inet_csk(sk);
     133           0 :         int err;
     134             : 
     135           0 :         err = -EEXIST;
     136           0 :         if (icsk->icsk_ulp_ops)
     137           0 :                 goto out_err;
     138             : 
     139           0 :         err = ulp_ops->init(sk);
     140           0 :         if (err)
     141           0 :                 goto out_err;
     142             : 
     143           0 :         icsk->icsk_ulp_ops = ulp_ops;
     144           0 :         return 0;
     145           0 : out_err:
     146           0 :         module_put(ulp_ops->owner);
     147           0 :         return err;
     148             : }
     149             : 
     150           0 : int tcp_set_ulp(struct sock *sk, const char *name)
     151             : {
     152           0 :         const struct tcp_ulp_ops *ulp_ops;
     153             : 
     154           0 :         sock_owned_by_me(sk);
     155             : 
     156           0 :         ulp_ops = __tcp_ulp_find_autoload(name);
     157           0 :         if (!ulp_ops)
     158             :                 return -ENOENT;
     159             : 
     160           0 :         return __tcp_set_ulp(sk, ulp_ops);
     161             : }

Generated by: LCOV version 1.14