linux socket can code flow

使用socket can一年多的时间,因为与不同设备的通信会把驱动改成uart can spi can等兼容user space接口,抽时间梳理了一下socket can的code flow 

socket can 接收流程:

read(socket_fd)

[<c0648c64>] (__skb_wait_for_more_packets) from [<c0649d0c>] (__skb_recv_datagram+0x8c/0xa0)
[<c0649d0c>] (__skb_recv_datagram) from [<c0649d54>] (skb_recv_datagram+0x34/0x3c) 
[<c0649d54>] (skb_recv_datagram) from [<c074d3d0>] (raw_recvmsg+0x30/0x154)        
[<c074d3d0>] (raw_recvmsg) from [<c0639244>] (sock_read_iter+0x80/0xa8)        
[<c0639244>] (sock_read_iter) from [<c022ebd8>] (__vfs_read+0xf0/0x118) 
[<c022ebd8>] (__vfs_read) from [<c022ec90>] (vfs_read+0x90/0xfc)   
[<c022ec90>] (vfs_read) from [<c022f0bc>] (SyS_read+0x3c/0x74)       
[<c022f0bc>] (SyS_read) from [<c01070a0>] (ret_fast_syscall+0x0/0x48)    

user space的socket can读接口是sleep wait在网络层,哪里把它唤醒的呢?

我们先确认清楚它到底睡在什么地方,__skb_wait_for_more_packets,中调用prepare_to_wait_exclusive 睡在 sk_sleep(sock)上,实质是sk->sk_wq

int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
				const struct sk_buff *skb)
{
	int error;
	DEFINE_WAIT_FUNC(wait, receiver_wake_function);

	prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
    schedule_timeout();
}

static inline wait_queue_head_t *sk_sleep(struct sock *sk)
{
	BUILD_BUG_ON(offsetof(struct socket_wq, wait) != 0);
	return &rcu_dereference_raw(sk->sk_wq)->wait;
}

sk->sk_wq,每一个sock初始化的都是都有自己的等待队列,can sock的初始can_create --> sock_init_data ,其中还有一处sk->sk_data_ready    =    sock_def_readable,这个函数是code flow的关键,卖个关子继续向下看

static int can_create(struct net *net, struct socket *sock, int protocol,
		      int kern)
{

	cp = can_get_proto(protocol);

	sock->ops = cp->ops;

	sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, kern);

	sock_init_data(sock, sk);
	sk->sk_destruct = can_sock_destruct;
	
}

void sock_init_data(struct socket *sock, struct sock *sk)
{
	sk_init_common(sk);

	sk->sk_allocation	=	GFP_KERNEL;
	sk->sk_rcvbuf		=	sysctl_rmem_default;
	sk->sk_sndbuf		=	sysctl_wmem_default;
	sk->sk_state		=	TCP_CLOSE;
    sk->sk_data_ready	=	sock_def_readable;
	sk_set_socket(sk, sock);

	if (sock) {
		sk->sk_type	=	sock->type;
		sk->sk_wq	=	sock->wq;
		sock->sk	=	sk;
		sk->sk_uid	=	SOCK_INODE(sock)->i_uid;
	} else {
		sk->sk_wq	=	NULL;
		sk->sk_uid	=	make_kuid(sock_net(sk)->user_ns, 0);
	}

知道接收线程睡的什么地方后就方便找唤醒的code flow了。

netif_receive_skb 把底层驱动收到的skb传递到网络层,是这个函数把user space唤醒起来收包的?

[<c074a564>] (can_receive) from [<c074a604>] (can_rcv+0x74/0x9c)
[<c074a604>] (can_rcv) from [<c0652948>] (__netif_receive_skb_core+0x8dc/0xae0)
[<c0652948>] (__netif_receive_skb_core) from [<c0657c30>] (netif_receive_skb_internal+0x64/0x128)
[<c0657c30>] (netif_receive_skb_internal) from [<bf000588>] (work_fn_transf+0x2fc/0x3dc [spican])

__netif_receive_skb_core --> can_receive --> can_rcv_filter --> deliver_skb --> can_packet->func --> can_rcv

can_rcv_filter 会讲收到的skb分发到不同的监听设备上去,比如 filter,错误帧,扩展帧等,user space有接口可以查看每个接收设备,可见接收所有报文的socket can有三个都是can0 设备上打开的socket

~ # cat /proc/net/can/
rcvlist_all  rcvlist_eff  rcvlist_err  rcvlist_fil  rcvlist_inv  rcvlist_sff  reset_stats  stats        version

~ # cat /proc/net/can/rcvlist_all 

receive list 'rx_all':
  (any: no entry)
  (can0: no entry)
  device   can_id   can_mask  function  userdata   matches  ident
   can0      000    00000000  c07f703c  c5ae7000   5284958  raw
   can0      000    00000000  c07f703c  c5ad0800   5287093  raw
   can0      000    00000000  c07f703c  c5ad0400   5287094  raw

 

static struct packet_type can_packet __read_mostly = {
     .type = cpu_to_be16(ETH_P_CAN),
     .func = can_rcv,
};

static void can_receive(struct sk_buff *skb, struct net_device *dev)
{

	/* update statistics */
	can_stats->rx_frames++;
	can_stats->rx_frames_delta++;

	/* deliver the packet to sockets listening on all devices */
	matches = can_rcv_filter(net->can.can_rx_alldev_list, skb);

	/* find receive list for this device */
	d = find_dev_rcv_lists(net, dev);
	if (d)
		matches += can_rcv_filter(d, skb);

	/* consume the skbuff allocated by the netdevice driver */
	consume_skb(skb);

}

static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
{
	struct receiver *r;
	int matches = 0;
	struct can_frame *cf = (struct can_frame *)skb->data;
	canid_t can_id = cf->can_id;


	if (can_id & CAN_ERR_FLAG) {
		/* check for error message frame entries only */
		hlist_for_each_entry_rcu(r, &d->rx[RX_ERR], list) {
			if (can_id & r->mask) {
				deliver(skb, r);
				matches++;
			}
		}
		return matches;
	}

	/* check for unfiltered entries */
	hlist_for_each_entry_rcu(r, &d->rx[RX_ALL], list) {
		deliver(skb, r);
		matches++;
	}

	/* check for can_id/mask entries */
	hlist_for_each_entry_rcu(r, &d->rx[RX_FIL], list) {
		if ((can_id & r->mask) == r->can_id) {
			deliver(skb, r);
			matches++;
		}
	}

	/* check for inverted can_id/mask entries */
	hlist_for_each_entry_rcu(r, &d->rx[RX_INV], list) {
		if ((can_id & r->mask) != r->can_id) {
			deliver(skb, r);
			matches++;
		}
	}

	/* check filterlists for single non-RTR can_ids */
	if (can_id & CAN_RTR_FLAG)
		return matches;

	if (can_id & CAN_EFF_FLAG) {
		hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {
			if (r->can_id == can_id) {
				deliver(skb, r);
				matches++;
			}
		}
	} else {
		can_id &= CAN_SFF_MASK;
		hlist_for_each_entry_rcu(r, &d->rx_sff[can_id], list) {
			deliver(skb, r);
			matches++;
		}
	}

	return matches;
}

static inline void deliver(struct sk_buff *skb, struct receiver *r)
{
	r->func(skb, r->data);
	r->matches++;
}

receiver 是netif_receive_skb code flow的最后一棒,不同接收类型的设备的receiver分别是什么是何时注册到dev_rcv_lists中的呢?

default register
raw_bind --> raw_enable_allfilters --> raw_enable_filters --> can_rx_register
setsocketopt 
raw_setsockopt --> raw_enable_allfilters --> raw_enable_filters --> can_rx_register

对应userspace 设置socket 通信步骤中的 bind 与setsockopt函数注册的receiver

由如下code flow可知can_rx_register 注册的接收设备的接收函数是raw_rcv。

static int raw_enable_filters(struct net *net, struct net_device *dev,
			      struct sock *sk, struct can_filter *filter,
			      int count)
{

	for (i = 0; i < count; i++) {
		err = can_rx_register(net, dev, filter[i].can_id,
				      filter[i].can_mask,
				      raw_rcv, sk, "raw", sk);
		if (err) {
			/* clean up successfully registered filters */
			while (--i >= 0)
				can_rx_unregister(net, dev, filter[i].can_id,
						  filter[i].can_mask,
						  raw_rcv, sk);
			break;
		}
	}

	return err;
}


/**
 * can_rx_register - subscribe CAN frames from a specific interface
 * @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
 * @can_id: CAN identifier (see description)
 * @mask: CAN mask (see description)
 * @func: callback function on filter match
 * @data: returned parameter for callback function
 * @ident: string for calling module identification
 * @sk: socket pointer (might be NULL)
 *
 * Description:
 *  Invokes the callback function with the received sk_buff and the given
 *  parameter 'data' on a matching receive filter. A filter matches, when
 *
 *          <received_can_id> & mask == can_id & mask
 *
 *  The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
 *  filter for error message frames (CAN_ERR_FLAG bit set in mask).
 *
 *  The provided pointer to the sk_buff is guaranteed to be valid as long as
 *  the callback function is running. The callback function must *not* free
 *  the given sk_buff while processing it's task. When the given sk_buff is
 *  needed after the end of the callback function it must be cloned inside
 *  the callback function with skb_clone().
 *
 * Return:
 *  0 on success
 *  -ENOMEM on missing cache mem to create subscription entry
 *  -ENODEV unknown device
 */
int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
		    canid_t mask, void (*func)(struct sk_buff *, void *),
		    void *data, char *ident, struct sock *sk)
{

	r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);

	d = find_dev_rcv_lists(net, dev);
	if (d) {
		rl = find_rcv_list(&can_id, &mask, d);

		r->can_id  = can_id;
		r->mask    = mask;
		r->matches = 0;
		r->func    = func;
		r->data    = data;
		r->ident   = ident;
		r->sk      = sk;

		hlist_add_head_rcu(&r->list, rl);
		d->entries++;

	return err;
}
EXPORT_SYMBOL(can_rx_register);

raw_rcv函数把skb放到对应的socket skb queue中,然后调用了sk->sk_data_ready 即 sock_def_readable,该函数里面就会wakeup睡在sk->sk_wq等待队列上的线程。破案!!!

static void raw_rcv(struct sk_buff *oskb, void *data)
{

	/* clone the given skb to be able to enqueue it into the rcv queue */
	skb = skb_clone(oskb, GFP_ATOMIC);
	if (!skb)
		return;

	/*
	 *  Put the datagram to the queue so that raw_recvmsg() can
	 *  get it from there.  We need to pass the interface index to
	 *  raw_recvmsg().  We pass a whole struct sockaddr_can in skb->cb
	 *  containing the interface index.
	 */

	sock_skb_cb_check_size(sizeof(struct sockaddr_can));
	addr = (struct sockaddr_can *)skb->cb;
	memset(addr, 0, sizeof(*addr));
	addr->can_family  = AF_CAN;
	addr->can_ifindex = skb->dev->ifindex;

	/* add CAN specific message flags for raw_recvmsg() */
	pflags = raw_flags(skb);
	*pflags = 0;
	if (oskb->sk)
		*pflags |= MSG_DONTROUTE;
	if (oskb->sk == sk)
		*pflags |= MSG_CONFIRM;

	if (sock_queue_rcv_skb(sk, skb) < 0)
		kfree_skb(skb);
}

int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{

	spin_lock_irqsave(&list->lock, flags);
	sock_skb_set_dropcount(sk, skb);
	__skb_queue_tail(list, skb);
	spin_unlock_irqrestore(&list->lock, flags);

	if (!sock_flag(sk, SOCK_DEAD))
		sk->sk_data_ready(sk);
	return 0;
}
EXPORT_SYMBOL(__sock_queue_rcv_skb);


static void sock_def_readable(struct sock *sk)
{
	struct socket_wq *wq;

	rcu_read_lock();
	wq = rcu_dereference(sk->sk_wq);
	if (skwq_has_sleeper(wq))
		wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI |
						POLLRDNORM | POLLRDBAND);
	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	rcu_read_unlock();
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

spican socket can userspace can发送流程

[<c04ed484>] (spi_transfer_one_message) from [<c04edeec>] (__spi_pump_messages+0x6b0/0x6ec)
[<c04edeec>] (__spi_pump_messages) from [<c04ef25c>] (__spi_sync+0x1f4/0x268)
[<c04ef25c>] (__spi_sync) from [<c04ef2f4>] (spi_sync+0x24/0x3c)
[<c04ef2f4>] (spi_sync) from [<bf000470>] (can_spi_transfer.constprop.1+0x1e4/0x22c [spican])
[<bf000470>] (can_spi_transfer.constprop.1 [spican]) from [<bf0008e8>] (canspi_write+0x54/0x90 [spican])
[<bf0008e8>] (canspi_write [spican]) from [<bf000968>] (st_can_start_xmit+0x44/0x6dc [spican])
[<bf000968>] (st_can_start_xmit [spican]) from [<c0656a5c>] (dev_hard_start_xmit+0x108/0x268)
[<c0656a5c>] (dev_hard_start_xmit) from [<c067b19c>] (sch_direct_xmit+0x98/0x17c)
[<c067b19c>] (sch_direct_xmit) from [<c0656f60>] (__dev_queue_xmit+0x2e4/0x588)
[<c0656f60>] (__dev_queue_xmit) from [<c0749b24>] (can_send+0x160/0x1e8)
[<c0749b24>] (can_send) from [<c074d634>] (raw_sendmsg+0x140/0x1b4)
[<c074d634>] (raw_sendmsg) from [<c063a630>] (sock_sendmsg+0x14/0x24)
[<c063a630>] (sock_sendmsg) from [<c063a6c4>] (sock_write_iter+0x84/0xac)
[<c063a6c4>] (sock_write_iter) from [<c022ee20>] (__vfs_write+0xf0/0x11c)
[<c022ee20>] (__vfs_write) from [<c022efc0>] (vfs_write+0xb8/0x144)
[<c022efc0>] (vfs_write) from [<c022f130>] (SyS_write+0x3c/0x74)
[<c022f130>] (SyS_write) from [<c01070a0>] (ret_fast_syscall+0x0/0x48)

socket can 发送数据流向  拥塞控制qdisc,软中断net_tx_sofrirq(ksoftirqd软中断线程) 

源码路径net/can/af_can.c  net/core/*  net/sched/*  net/unix/*  drivers/net/can

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenhuxi_yu

感谢投币,继续输出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值