123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- #include <linux/kernel.h>
- #include <linux/string.h>
- #include <linux/socket.h>
- #include <linux/un.h>
- #include <linux/net.h>
- #include <linux/fs.h>
- #include <linux/skbuff.h>
- #include <linux/netdevice.h>
- #include <linux/file.h>
- #include <linux/proc_fs.h>
- #include <linux/mutex.h>
- #include <linux/wait.h>
- #include <net/sock.h>
- #include <net/af_unix.h>
- #include <net/scm.h>
- #include <net/tcp_states.h>
- static LIST_HEAD(gc_inflight_list);
- static LIST_HEAD(gc_candidates);
- static DEFINE_SPINLOCK(unix_gc_lock);
- static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
- unsigned int unix_tot_inflight;
- struct sock *unix_get_socket(struct file *filp)
- {
- struct sock *u_sock = NULL;
- struct inode *inode = file_inode(filp);
-
- if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
- struct socket *sock = SOCKET_I(inode);
- struct sock *s = sock->sk;
-
- if (s && sock->ops && sock->ops->family == PF_UNIX)
- u_sock = s;
- }
- return u_sock;
- }
- void unix_inflight(struct user_struct *user, struct file *fp)
- {
- struct sock *s = unix_get_socket(fp);
- spin_lock(&unix_gc_lock);
- if (s) {
- struct unix_sock *u = unix_sk(s);
- if (atomic_long_inc_return(&u->inflight) == 1) {
- BUG_ON(!list_empty(&u->link));
- list_add_tail(&u->link, &gc_inflight_list);
- } else {
- BUG_ON(list_empty(&u->link));
- }
- unix_tot_inflight++;
- }
- user->unix_inflight++;
- spin_unlock(&unix_gc_lock);
- }
- void unix_notinflight(struct user_struct *user, struct file *fp)
- {
- struct sock *s = unix_get_socket(fp);
- spin_lock(&unix_gc_lock);
- if (s) {
- struct unix_sock *u = unix_sk(s);
- BUG_ON(!atomic_long_read(&u->inflight));
- BUG_ON(list_empty(&u->link));
- if (atomic_long_dec_and_test(&u->inflight))
- list_del_init(&u->link);
- unix_tot_inflight--;
- }
- user->unix_inflight--;
- spin_unlock(&unix_gc_lock);
- }
- static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
- struct sk_buff_head *hitlist)
- {
- struct sk_buff *skb;
- struct sk_buff *next;
- spin_lock(&x->sk_receive_queue.lock);
- skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
-
- if (UNIXCB(skb).fp) {
- bool hit = false;
-
- int nfd = UNIXCB(skb).fp->count;
- struct file **fp = UNIXCB(skb).fp->fp;
- while (nfd--) {
-
- struct sock *sk = unix_get_socket(*fp++);
- if (sk) {
- struct unix_sock *u = unix_sk(sk);
-
- if (test_bit(UNIX_GC_CANDIDATE, &u->gc_flags)) {
- hit = true;
- func(u);
- }
- }
- }
- if (hit && hitlist != NULL) {
- __skb_unlink(skb, &x->sk_receive_queue);
- __skb_queue_tail(hitlist, skb);
- }
- }
- }
- spin_unlock(&x->sk_receive_queue.lock);
- }
- static void scan_children(struct sock *x, void (*func)(struct unix_sock *),
- struct sk_buff_head *hitlist)
- {
- if (x->sk_state != TCP_LISTEN) {
- scan_inflight(x, func, hitlist);
- } else {
- struct sk_buff *skb;
- struct sk_buff *next;
- struct unix_sock *u;
- LIST_HEAD(embryos);
-
- spin_lock(&x->sk_receive_queue.lock);
- skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
- u = unix_sk(skb->sk);
-
- BUG_ON(!list_empty(&u->link));
- list_add_tail(&u->link, &embryos);
- }
- spin_unlock(&x->sk_receive_queue.lock);
- while (!list_empty(&embryos)) {
- u = list_entry(embryos.next, struct unix_sock, link);
- scan_inflight(&u->sk, func, hitlist);
- list_del_init(&u->link);
- }
- }
- }
- static void dec_inflight(struct unix_sock *usk)
- {
- atomic_long_dec(&usk->inflight);
- }
- static void inc_inflight(struct unix_sock *usk)
- {
- atomic_long_inc(&usk->inflight);
- }
- static void inc_inflight_move_tail(struct unix_sock *u)
- {
- atomic_long_inc(&u->inflight);
-
- if (test_bit(UNIX_GC_MAYBE_CYCLE, &u->gc_flags))
- list_move_tail(&u->link, &gc_candidates);
- }
- static bool gc_in_progress;
- #define UNIX_INFLIGHT_TRIGGER_GC 16000
- void wait_for_unix_gc(void)
- {
-
- if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
- unix_gc();
- wait_event(unix_gc_wait, gc_in_progress == false);
- }
- void unix_gc(void)
- {
- struct unix_sock *u;
- struct unix_sock *next;
- struct sk_buff_head hitlist;
- struct list_head cursor;
- LIST_HEAD(not_cycle_list);
- spin_lock(&unix_gc_lock);
-
- if (gc_in_progress)
- goto out;
- gc_in_progress = true;
-
- list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
- long total_refs;
- long inflight_refs;
- total_refs = file_count(u->sk.sk_socket->file);
- inflight_refs = atomic_long_read(&u->inflight);
- BUG_ON(inflight_refs < 1);
- BUG_ON(total_refs < inflight_refs);
- if (total_refs == inflight_refs) {
- list_move_tail(&u->link, &gc_candidates);
- __set_bit(UNIX_GC_CANDIDATE, &u->gc_flags);
- __set_bit(UNIX_GC_MAYBE_CYCLE, &u->gc_flags);
- }
- }
-
- list_for_each_entry(u, &gc_candidates, link)
- scan_children(&u->sk, dec_inflight, NULL);
-
- list_add(&cursor, &gc_candidates);
- while (cursor.next != &gc_candidates) {
- u = list_entry(cursor.next, struct unix_sock, link);
-
- list_move(&cursor, &u->link);
- if (atomic_long_read(&u->inflight) > 0) {
- list_move_tail(&u->link, ¬_cycle_list);
- __clear_bit(UNIX_GC_MAYBE_CYCLE, &u->gc_flags);
- scan_children(&u->sk, inc_inflight_move_tail, NULL);
- }
- }
- list_del(&cursor);
-
- skb_queue_head_init(&hitlist);
- list_for_each_entry(u, &gc_candidates, link)
- scan_children(&u->sk, inc_inflight, &hitlist);
-
- while (!list_empty(¬_cycle_list)) {
- u = list_entry(not_cycle_list.next, struct unix_sock, link);
- __clear_bit(UNIX_GC_CANDIDATE, &u->gc_flags);
- list_move_tail(&u->link, &gc_inflight_list);
- }
- spin_unlock(&unix_gc_lock);
-
- __skb_queue_purge(&hitlist);
- spin_lock(&unix_gc_lock);
-
- BUG_ON(!list_empty(&gc_candidates));
- gc_in_progress = false;
- wake_up(&unix_gc_wait);
- out:
- spin_unlock(&unix_gc_lock);
- }
|