Skip to content
sock.c 89.6 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
void sock_init_data(struct socket *sock, struct sock *sk)
{
Linus Torvalds's avatar
Linus Torvalds committed
	sk->sk_send_head	=	NULL;

	timer_setup(&sk->sk_timer, NULL, 0);
Linus Torvalds's avatar
Linus Torvalds committed
	sk->sk_allocation	=	GFP_KERNEL;
	sk->sk_rcvbuf		=	sysctl_rmem_default;
	sk->sk_sndbuf		=	sysctl_wmem_default;
	sk->sk_state		=	TCP_CLOSE;
	sk_set_socket(sk, sock);
Linus Torvalds's avatar
Linus Torvalds committed

	sock_set_flag(sk, SOCK_ZAPPED);

	if (sock) {
Linus Torvalds's avatar
Linus Torvalds committed
		sk->sk_type	=	sock->type;
		RCU_INIT_POINTER(sk->sk_wq, &sock->wq);
Linus Torvalds's avatar
Linus Torvalds committed
		sock->sk	=	sk;
		sk->sk_uid	=	SOCK_INODE(sock)->i_uid;
	} else {
		RCU_INIT_POINTER(sk->sk_wq, NULL);
		sk->sk_uid	=	make_kuid(sock_net(sk)->user_ns, 0);
	}
Linus Torvalds's avatar
Linus Torvalds committed

	rwlock_init(&sk->sk_callback_lock);
	if (sk->sk_kern_sock)
		lockdep_set_class_and_name(
			&sk->sk_callback_lock,
			af_kern_callback_keys + sk->sk_family,
			af_family_kern_clock_key_strings[sk->sk_family]);
	else
		lockdep_set_class_and_name(
			&sk->sk_callback_lock,
			af_callback_keys + sk->sk_family,
			af_family_clock_key_strings[sk->sk_family]);
Linus Torvalds's avatar
Linus Torvalds committed

	sk->sk_state_change	=	sock_def_wakeup;
	sk->sk_data_ready	=	sock_def_readable;
	sk->sk_write_space	=	sock_def_write_space;
	sk->sk_error_report	=	sock_def_error_report;
	sk->sk_destruct		=	sock_def_destruct;

	sk->sk_frag.page	=	NULL;
	sk->sk_frag.offset	=	0;
	sk->sk_peek_off		=	-1;
Linus Torvalds's avatar
Linus Torvalds committed

	sk->sk_peer_pid 	=	NULL;
	sk->sk_peer_cred	=	NULL;
Linus Torvalds's avatar
Linus Torvalds committed
	sk->sk_write_pending	=	0;
	sk->sk_rcvlowat		=	1;
	sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT;
	sk->sk_sndtimeo		=	MAX_SCHEDULE_TIMEOUT;

	sk->sk_stamp = SK_DEFAULT_STAMP;
#if BITS_PER_LONG==32
	seqlock_init(&sk->sk_stamp_seq);
#endif
Willem de Bruijn's avatar
Willem de Bruijn committed
	atomic_set(&sk->sk_zckey, 0);
Linus Torvalds's avatar
Linus Torvalds committed

#ifdef CONFIG_NET_RX_BUSY_POLL
	sk->sk_napi_id		=	0;
	sk->sk_ll_usec		=	sysctl_net_busy_read;
	sk->sk_max_pacing_rate = ~0UL;
	sk->sk_pacing_rate = ~0UL;
	WRITE_ONCE(sk->sk_pacing_shift, 10);
	sk->sk_incoming_cpu = -1;
Eric Dumazet's avatar
Eric Dumazet committed
	/*
	 * Before updating sk_refcnt, we must commit prior changes to memory
	 * (Documentation/RCU/rculist_nulls.rst for details)
Eric Dumazet's avatar
Eric Dumazet committed
	 */
	smp_wmb();
	refcount_set(&sk->sk_refcnt, 1);
	atomic_set(&sk->sk_drops, 0);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(sock_init_data);
Linus Torvalds's avatar
Linus Torvalds committed

void lock_sock_nested(struct sock *sk, int subclass)
Linus Torvalds's avatar
Linus Torvalds committed
{
	might_sleep();
	spin_lock_bh(&sk->sk_lock.slock);
Linus Torvalds's avatar
Linus Torvalds committed
		__lock_sock(sk);
	spin_unlock(&sk->sk_lock.slock);
	/*
	 * The sk_lock has mutex_lock() semantics here:
	 */
	mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
	local_bh_enable();
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(lock_sock_nested);
Linus Torvalds's avatar
Linus Torvalds committed

void release_sock(struct sock *sk)
Linus Torvalds's avatar
Linus Torvalds committed
{
	spin_lock_bh(&sk->sk_lock.slock);
Linus Torvalds's avatar
Linus Torvalds committed
	if (sk->sk_backlog.tail)
		__release_sock(sk);
	/* Warning : release_cb() might need to release sk ownership,
	 * ie call sock_release_ownership(sk) before us.
	 */
Eric Dumazet's avatar
Eric Dumazet committed
	if (sk->sk_prot->release_cb)
		sk->sk_prot->release_cb(sk);

	sock_release_ownership(sk);
	if (waitqueue_active(&sk->sk_lock.wq))
		wake_up(&sk->sk_lock.wq);
	spin_unlock_bh(&sk->sk_lock.slock);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(release_sock);

/**
 * lock_sock_fast - fast version of lock_sock
 * @sk: socket
 *
 * This version should be used for very small section, where process wont block
 * return false if fast path is taken:
 *
 *   sk_lock.slock locked, owned = 0, BH disabled
 *
 * return true if slow path is taken:
 *
 *   sk_lock.slock unlocked, owned = 1, BH enabled
 */
bool lock_sock_fast(struct sock *sk) __acquires(&sk->sk_lock.slock)
{
	might_sleep();
	spin_lock_bh(&sk->sk_lock.slock);

	if (!sk->sk_lock.owned)
		/*
		 * Note : We must disable BH
		 */
		return false;

	__lock_sock(sk);
	sk->sk_lock.owned = 1;
	spin_unlock(&sk->sk_lock.slock);
	/*
	 * The sk_lock has mutex_lock() semantics here:
	 */
	mutex_acquire(&sk->sk_lock.dep_map, 0, 0, _RET_IP_);
	__acquire(&sk->sk_lock.slock);
	local_bh_enable();
	return true;
}
EXPORT_SYMBOL(lock_sock_fast);

int sock_gettstamp(struct socket *sock, void __user *userstamp,
		   bool timeval, bool time32)
	struct sock *sk = sock->sk;
	struct timespec64 ts;

	sock_enable_timestamp(sk, SOCK_TIMESTAMP);
	ts = ktime_to_timespec64(sock_read_timestamp(sk));
	if (ts.tv_sec == -1)
Linus Torvalds's avatar
Linus Torvalds committed
		return -ENOENT;
	if (ts.tv_sec == 0) {
		ktime_t kt = ktime_get_real();
		sock_write_timestamp(sk, kt);
		ts = ktime_to_timespec64(kt);
Linus Torvalds's avatar
Linus Torvalds committed

	if (timeval)
		ts.tv_nsec /= 1000;
#ifdef CONFIG_COMPAT_32BIT_TIME
	if (time32)
		return put_old_timespec32(&ts, userstamp);
#endif
#ifdef CONFIG_SPARC64
	/* beware of padding in sparc64 timeval */
	if (timeval && !in_compat_syscall()) {
		struct __kernel_old_timeval __user tv = {
			.tv_sec = ts.tv_sec,
			.tv_usec = ts.tv_nsec,
		if (copy_to_user(userstamp, &tv, sizeof(tv)))
			return -EFAULT;
		return 0;
#endif
	return put_timespec64(&ts, userstamp);
EXPORT_SYMBOL(sock_gettstamp);
void sock_enable_timestamp(struct sock *sk, enum sock_flags flag)
	if (!sock_flag(sk, flag)) {
		unsigned long previous_flags = sk->sk_flags;

		sock_set_flag(sk, flag);
		/*
		 * we just set one of the two flags which require net
		 * time stamping, but time stamping might have been on
		 * already because of the other one
		 */
		if (sock_needs_netstamp(sk) &&
		    !(previous_flags & SK_FLAGS_TIMESTAMP))
			net_enable_timestamp();
int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
		       int level, int type)
{
	struct sock_exterr_skb *serr;
	struct sk_buff *skb;
	skb = sock_dequeue_err_skb(sk);
	if (skb == NULL)
		goto out;

	copied = skb->len;
	if (copied > len) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}
	err = skb_copy_datagram_msg(skb, 0, msg, copied);
	if (err)
		goto out_free_skb;

	sock_recv_timestamp(msg, sk, skb);

	serr = SKB_EXT_ERR(skb);
	put_cmsg(msg, level, type, sizeof(serr->ee), &serr->ee);

	msg->msg_flags |= MSG_ERRQUEUE;
	err = copied;

out_free_skb:
	kfree_skb(skb);
out:
	return err;
}
EXPORT_SYMBOL(sock_recv_errqueue);

Linus Torvalds's avatar
Linus Torvalds committed
/*
 *	Get a socket option on an socket.
 *
 *	FIX: POSIX 1003.1g is very ambiguous here. It states that
 *	asynchronous errors should be reported by getsockopt. We assume
 *	this means if you specify SO_ERROR (otherwise whats the point of it).
 */
int sock_common_getsockopt(struct socket *sock, int level, int optname,
			   char __user *optval, int __user *optlen)
{
	struct sock *sk = sock->sk;

	return sk->sk_prot->getsockopt(sk, level, optname, optval, optlen);
}
EXPORT_SYMBOL(sock_common_getsockopt);

int sock_common_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
			int flags)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct sock *sk = sock->sk;
	int addr_len = 0;
	int err;

	err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT,
Linus Torvalds's avatar
Linus Torvalds committed
				   flags & ~MSG_DONTWAIT, &addr_len);
	if (err >= 0)
		msg->msg_namelen = addr_len;
	return err;
}
EXPORT_SYMBOL(sock_common_recvmsg);

/*
 *	Set socket options on an inet socket.
 */
int sock_common_setsockopt(struct socket *sock, int level, int optname,
			   sockptr_t optval, unsigned int optlen)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct sock *sk = sock->sk;

	return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen);
}
EXPORT_SYMBOL(sock_common_setsockopt);

void sk_common_release(struct sock *sk)
{
	if (sk->sk_prot->destroy)
		sk->sk_prot->destroy(sk);

	/*
Miaohe Lin's avatar
Miaohe Lin committed
	 * Observation: when sk_common_release is called, processes have
Linus Torvalds's avatar
Linus Torvalds committed
	 * no access to socket. But net still has.
	 * Step one, detach it from networking:
	 *
	 * A. Remove from hash tables.
	 */

	sk->sk_prot->unhash(sk);

	/*
	 * In this point socket cannot receive new packets, but it is possible
	 * that some packets are in flight because some CPU runs receiver and
	 * did hash table lookup before we unhashed socket. They will achieve
	 * receive queue and will be purged by socket destructor.
	 *
	 * Also we still have packets pending on receive queue and probably,
	 * our own packets waiting in device queues. sock_destroy will drain
	 * receive queue, but transmitted packets will delay socket destruction
	 * until the last reference will be released.
	 */

	sock_orphan(sk);

	xfrm_sk_free_policy(sk);

	sk_refcnt_debug_release(sk);
Linus Torvalds's avatar
Linus Torvalds committed
	sock_put(sk);
}
EXPORT_SYMBOL(sk_common_release);

void sk_get_meminfo(const struct sock *sk, u32 *mem)
{
	memset(mem, 0, sizeof(*mem) * SK_MEMINFO_VARS);

	mem[SK_MEMINFO_RMEM_ALLOC] = sk_rmem_alloc_get(sk);
	mem[SK_MEMINFO_RCVBUF] = READ_ONCE(sk->sk_rcvbuf);
	mem[SK_MEMINFO_WMEM_ALLOC] = sk_wmem_alloc_get(sk);
	mem[SK_MEMINFO_SNDBUF] = READ_ONCE(sk->sk_sndbuf);
	mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc;
	mem[SK_MEMINFO_WMEM_QUEUED] = READ_ONCE(sk->sk_wmem_queued);
	mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc);
	mem[SK_MEMINFO_BACKLOG] = READ_ONCE(sk->sk_backlog.len);
	mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops);
}

#ifdef CONFIG_PROC_FS
#define PROTO_INUSE_NR	64	/* should be enough for the first time */
struct prot_inuse {
	int val[PROTO_INUSE_NR];
};

static DECLARE_BITMAP(proto_inuse_idx, PROTO_INUSE_NR);

void sock_prot_inuse_add(struct net *net, struct proto *prot, int val)
{
	__this_cpu_add(net->core.prot_inuse->val[prot->inuse_idx], val);
}
EXPORT_SYMBOL_GPL(sock_prot_inuse_add);

int sock_prot_inuse_get(struct net *net, struct proto *prot)
{
	int cpu, idx = prot->inuse_idx;
	int res = 0;

	for_each_possible_cpu(cpu)
		res += per_cpu_ptr(net->core.prot_inuse, cpu)->val[idx];

	return res >= 0 ? res : 0;
}
EXPORT_SYMBOL_GPL(sock_prot_inuse_get);

static void sock_inuse_add(struct net *net, int val)
{
	this_cpu_add(*net->core.sock_inuse, val);
}

int sock_inuse_get(struct net *net)
{
	int cpu, res = 0;

	for_each_possible_cpu(cpu)
		res += *per_cpu_ptr(net->core.sock_inuse, cpu);

	return res;
}

EXPORT_SYMBOL_GPL(sock_inuse_get);

static int __net_init sock_inuse_init_net(struct net *net)
	net->core.prot_inuse = alloc_percpu(struct prot_inuse);
	if (net->core.prot_inuse == NULL)
		return -ENOMEM;

	net->core.sock_inuse = alloc_percpu(int);
	if (net->core.sock_inuse == NULL)
		goto out;

	return 0;

out:
	free_percpu(net->core.prot_inuse);
	return -ENOMEM;
static void __net_exit sock_inuse_exit_net(struct net *net)
	free_percpu(net->core.prot_inuse);
	free_percpu(net->core.sock_inuse);
}

static struct pernet_operations net_inuse_ops = {
	.init = sock_inuse_init_net,
	.exit = sock_inuse_exit_net,
};

static __init int net_inuse_init(void)
{
	if (register_pernet_subsys(&net_inuse_ops))
		panic("Cannot initialize net inuse counters");

	return 0;
}

core_initcall(net_inuse_init);
static int assign_proto_idx(struct proto *prot)
{
	prot->inuse_idx = find_first_zero_bit(proto_inuse_idx, PROTO_INUSE_NR);

	if (unlikely(prot->inuse_idx == PROTO_INUSE_NR - 1)) {
Joe Perches's avatar
Joe Perches committed
		pr_err("PROTO_INUSE_NR exhausted\n");
}

static void release_proto_idx(struct proto *prot)
{
	if (prot->inuse_idx != PROTO_INUSE_NR - 1)
		clear_bit(prot->inuse_idx, proto_inuse_idx);
}
#else
static inline int assign_proto_idx(struct proto *prot)
}

static inline void release_proto_idx(struct proto *prot)
{
}

static void sock_inuse_add(struct net *net, int val)
{
}
static void tw_prot_cleanup(struct timewait_sock_ops *twsk_prot)
{
	if (!twsk_prot)
		return;
	kfree(twsk_prot->twsk_slab_name);
	twsk_prot->twsk_slab_name = NULL;
	kmem_cache_destroy(twsk_prot->twsk_slab);
	twsk_prot->twsk_slab = NULL;
}

static int tw_prot_init(const struct proto *prot)
{
	struct timewait_sock_ops *twsk_prot = prot->twsk_prot;

	if (!twsk_prot)
		return 0;

	twsk_prot->twsk_slab_name = kasprintf(GFP_KERNEL, "tw_sock_%s",
					      prot->name);
	if (!twsk_prot->twsk_slab_name)
		return -ENOMEM;

	twsk_prot->twsk_slab =
		kmem_cache_create(twsk_prot->twsk_slab_name,
				  twsk_prot->twsk_obj_size, 0,
				  SLAB_ACCOUNT | prot->slab_flags,
				  NULL);
	if (!twsk_prot->twsk_slab) {
		pr_crit("%s: Can't create timewait sock SLAB cache!\n",
			prot->name);
		return -ENOMEM;
	}

	return 0;
}

static void req_prot_cleanup(struct request_sock_ops *rsk_prot)
{
	if (!rsk_prot)
		return;
	kfree(rsk_prot->slab_name);
	rsk_prot->slab_name = NULL;
	kmem_cache_destroy(rsk_prot->slab);
	rsk_prot->slab = NULL;
}

static int req_prot_init(const struct proto *prot)
{
	struct request_sock_ops *rsk_prot = prot->rsk_prot;

	if (!rsk_prot)
		return 0;

	rsk_prot->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s",
					prot->name);
	if (!rsk_prot->slab_name)
		return -ENOMEM;

	rsk_prot->slab = kmem_cache_create(rsk_prot->slab_name,
					   rsk_prot->obj_size, 0,
					   SLAB_ACCOUNT | prot->slab_flags,
					   NULL);

	if (!rsk_prot->slab) {
		pr_crit("%s: Can't create request sock SLAB cache!\n",
			prot->name);
		return -ENOMEM;
	}
	return 0;
}

int proto_register(struct proto *prot, int alloc_slab)
{
Linus Torvalds's avatar
Linus Torvalds committed
	if (alloc_slab) {
		prot->slab = kmem_cache_create_usercopy(prot->name,
					prot->obj_size, 0,
					SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT |
					prot->slab_flags,
					prot->useroffset, prot->usersize,
Linus Torvalds's avatar
Linus Torvalds committed

		if (prot->slab == NULL) {
Joe Perches's avatar
Joe Perches committed
			pr_crit("%s: Can't create sock SLAB cache!\n",
				prot->name);
Linus Torvalds's avatar
Linus Torvalds committed
		}
		if (req_prot_init(prot))
			goto out_free_request_sock_slab;
		if (tw_prot_init(prot))
			goto out_free_timewait_sock_slab;
	mutex_lock(&proto_list_mutex);
	ret = assign_proto_idx(prot);
	if (ret) {
		mutex_unlock(&proto_list_mutex);
		goto out_free_timewait_sock_slab;
Linus Torvalds's avatar
Linus Torvalds committed
	list_add(&prot->node, &proto_list);
	mutex_unlock(&proto_list_mutex);
out_free_timewait_sock_slab:
		tw_prot_cleanup(prot->twsk_prot);
	if (alloc_slab) {
		req_prot_cleanup(prot->rsk_prot);
		kmem_cache_destroy(prot->slab);
		prot->slab = NULL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(proto_register);

void proto_unregister(struct proto *prot)
{
	mutex_lock(&proto_list_mutex);
	list_del(&prot->node);
	mutex_unlock(&proto_list_mutex);
Linus Torvalds's avatar
Linus Torvalds committed

	kmem_cache_destroy(prot->slab);
	prot->slab = NULL;
Linus Torvalds's avatar
Linus Torvalds committed

	req_prot_cleanup(prot->rsk_prot);
	tw_prot_cleanup(prot->twsk_prot);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(proto_unregister);

int sock_load_diag_module(int family, int protocol)
{
	if (!protocol) {
		if (!sock_is_registered(family))
			return -ENOENT;

		return request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
				      NETLINK_SOCK_DIAG, family);
	}

#ifdef CONFIG_INET
	if (family == AF_INET &&
	    protocol != IPPROTO_RAW &&
	    protocol < MAX_INET_PROTOS &&
	    !rcu_access_pointer(inet_protos[protocol]))
		return -ENOENT;
#endif

	return request_module("net-pf-%d-proto-%d-type-%d-%d", PF_NETLINK,
			      NETLINK_SOCK_DIAG, family, protocol);
}
EXPORT_SYMBOL(sock_load_diag_module);

Linus Torvalds's avatar
Linus Torvalds committed
#ifdef CONFIG_PROC_FS
static void *proto_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(proto_list_mutex)
Linus Torvalds's avatar
Linus Torvalds committed
{
	mutex_lock(&proto_list_mutex);
	return seq_list_start_head(&proto_list, *pos);
Linus Torvalds's avatar
Linus Torvalds committed
}

static void *proto_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	return seq_list_next(v, &proto_list, pos);
Linus Torvalds's avatar
Linus Torvalds committed
}

static void proto_seq_stop(struct seq_file *seq, void *v)
	__releases(proto_list_mutex)
Linus Torvalds's avatar
Linus Torvalds committed
{
	mutex_unlock(&proto_list_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
}

static char proto_method_implemented(const void *method)
{
	return method == NULL ? 'n' : 'y';
}
static long sock_prot_memory_allocated(struct proto *proto)
{
	return proto->memory_allocated != NULL ? proto_memory_allocated(proto) : -1L;
static const char *sock_prot_memory_pressure(struct proto *proto)
{
	return proto->memory_pressure != NULL ?
	proto_memory_pressure(proto) ? "yes" : "no" : "NI";
}
Linus Torvalds's avatar
Linus Torvalds committed

static void proto_seq_printf(struct seq_file *seq, struct proto *proto)
{
Eric Dumazet's avatar
Eric Dumazet committed
	seq_printf(seq, "%-9s %4u %6d  %6ld   %-3s %6u   %-3s  %-10s "
Linus Torvalds's avatar
Linus Torvalds committed
			"%2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c\n",
		   proto->name,
		   proto->obj_size,
		   sock_prot_inuse_get(seq_file_net(seq), proto),
		   sock_prot_memory_allocated(proto),
		   sock_prot_memory_pressure(proto),
Linus Torvalds's avatar
Linus Torvalds committed
		   proto->max_header,
		   proto->slab == NULL ? "no" : "yes",
		   module_name(proto->owner),
		   proto_method_implemented(proto->close),
		   proto_method_implemented(proto->connect),
		   proto_method_implemented(proto->disconnect),
		   proto_method_implemented(proto->accept),
		   proto_method_implemented(proto->ioctl),
		   proto_method_implemented(proto->init),
		   proto_method_implemented(proto->destroy),
		   proto_method_implemented(proto->shutdown),
		   proto_method_implemented(proto->setsockopt),
		   proto_method_implemented(proto->getsockopt),
		   proto_method_implemented(proto->sendmsg),
		   proto_method_implemented(proto->recvmsg),
		   proto_method_implemented(proto->sendpage),
		   proto_method_implemented(proto->bind),
		   proto_method_implemented(proto->backlog_rcv),
		   proto_method_implemented(proto->hash),
		   proto_method_implemented(proto->unhash),
		   proto_method_implemented(proto->get_port),
		   proto_method_implemented(proto->enter_memory_pressure));
}

static int proto_seq_show(struct seq_file *seq, void *v)
{
Linus Torvalds's avatar
Linus Torvalds committed
		seq_printf(seq, "%-9s %-4s %-8s %-6s %-5s %-7s %-4s %-10s %s",
			   "protocol",
			   "size",
			   "sockets",
			   "memory",
			   "press",
			   "maxhdr",
			   "slab",
			   "module",
			   "cl co di ac io in de sh ss gs se re sp bi br ha uh gp em\n");
	else
		proto_seq_printf(seq, list_entry(v, struct proto, node));
Linus Torvalds's avatar
Linus Torvalds committed
	return 0;
}

static const struct seq_operations proto_seq_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.start  = proto_seq_start,
	.next   = proto_seq_next,
	.stop   = proto_seq_stop,
	.show   = proto_seq_show,
};

static __net_init int proto_init_net(struct net *net)
{
	if (!proc_create_net("protocols", 0444, net->proc_net, &proto_seq_ops,
			sizeof(struct seq_net_private)))
		return -ENOMEM;

	return 0;
}

static __net_exit void proto_exit_net(struct net *net)
{
	remove_proc_entry("protocols", net->proc_net);
}


static __net_initdata struct pernet_operations proto_net_ops = {
	.init = proto_init_net,
	.exit = proto_exit_net,
Linus Torvalds's avatar
Linus Torvalds committed
};

static int __init proto_init(void)
{
	return register_pernet_subsys(&proto_net_ops);
Linus Torvalds's avatar
Linus Torvalds committed
}

subsys_initcall(proto_init);

#endif /* PROC_FS */

#ifdef CONFIG_NET_RX_BUSY_POLL
bool sk_busy_loop_end(void *p, unsigned long start_time)
{
	struct sock *sk = p;

	return !skb_queue_empty_lockless(&sk->sk_receive_queue) ||
	       sk_busy_loop_timeout(sk, start_time);
}
EXPORT_SYMBOL(sk_busy_loop_end);
#endif /* CONFIG_NET_RX_BUSY_POLL */

int sock_bind_add(struct sock *sk, struct sockaddr *addr, int addr_len)
{
	if (!sk->sk_prot->bind_add)
		return -EOPNOTSUPP;
	return sk->sk_prot->bind_add(sk, addr, addr_len);
}
EXPORT_SYMBOL(sock_bind_add);