1 /* Copyright (c) 2011 The Regents of the University of California
2 * Barret Rhoden <brho@cs.berkeley.edu>
3 * See LICENSE for details.
5 * Kernel side of ucqs. */
13 /* Proc p needs to be current, and you should have checked that ucq is valid
14 * memory. We'll assert it here, to catch any of your bugs. =) */
15 void send_ucq_msg(struct ucq *ucq, struct proc *p, struct event_msg *msg)
17 uintptr_t my_slot = 0;
18 struct ucq_page *new_page, *old_page;
19 struct msg_container *my_msg;
21 assert(is_user_rwaddr(ucq, sizeof(struct ucq)));
22 /* So we can try to send ucqs to _Ss before they initialize */
23 if (!ucq->ucq_ready) {
24 if (p->state & (PROC_RUNNING_M | PROC_RUNNABLE_M))
25 warn("proc %d is _M with an uninitialized ucq %p\n", p->pid, ucq);
28 /* Bypass fetching/incrementing the counter if we're overflowing, helps
29 * prevent wraparound issues on the counter (only 12 bits of counter) */
30 if (ucq->prod_overflow)
32 /* Grab a potential slot */
33 my_slot = (uintptr_t)atomic_fetch_and_add(&ucq->prod_idx, 1);
34 if (slot_is_good(my_slot))
36 /* Warn others to not bother with the fetch_and_add */
37 ucq->prod_overflow = TRUE;
39 if (PGOFF(my_slot) > 3000)
40 warn("Abnormally high counter, there's probably something wrong!");
42 /* Lock, for this proc/ucq. Using an irqsave, since we may want to send ucq
43 * messages from irq context. */
44 hash_lock_irqsave(p->ucq_hashlock, (long)ucq);
45 /* Grab a potential slot (again, preventing another DoS) */
46 my_slot = (uintptr_t)atomic_fetch_and_add(&ucq->prod_idx, 1);
47 if (slot_is_good(my_slot))
49 /* Check to make sure the old_page was good before we do anything too
50 * intense (we deref it later). Bad pages are likely due to
51 * user-malfeasance or neglect.
53 * The is_user_rwaddr() check on old_page might catch addresses below
54 * MMAP_LOWEST_VA, and we can also handle a PF, but we'll explicitly check
55 * for 0 just to be sure (and it's a likely error). */
56 old_page = (struct ucq_page*)PTE_ADDR(my_slot);
57 if (!is_user_rwaddr(old_page, PGSIZE) || !old_page)
58 goto error_addr_unlock;
59 /* Things still aren't fixed, so we need to reset everything */
60 /* Try to get the spare page, so we don't have to mmap a new one */
61 new_page = (struct ucq_page*)atomic_swap(&ucq->spare_pg, 0);
63 /* Warn if we have a ridiculous amount of pages in the ucq */
64 if (atomic_fetch_and_add(&ucq->nr_extra_pgs, 1) > UCQ_WARN_THRESH)
65 warn("Over %d pages in ucq %p for pid %d!\n", UCQ_WARN_THRESH,
67 /* Giant warning: don't ask for anything other than anonymous memory at
68 * a non-fixed location. o/w, it may cause a TLB shootdown, which grabs
69 * the proc_lock, and potentially deadlock the system. */
70 new_page = (struct ucq_page*)do_mmap(p, 0, PGSIZE,
71 PROT_READ | PROT_WRITE,
72 MAP_ANON | MAP_POPULATE, 0, 0);
74 assert(!PGOFF(new_page));
76 /* If we're using the user-supplied new_page, we need to check it */
77 if (!is_user_rwaddr(new_page, PGSIZE) || PGOFF(new_page))
78 goto error_addr_unlock;
80 /* Now we have a page. Lets make sure it's set up properly */
81 new_page->header.cons_next_pg = 0;
82 new_page->header.nr_cons = 0;
83 /* Link the old page to the new one, so consumers know how to follow */
84 old_page->header.cons_next_pg = (uintptr_t)new_page;
85 /* Set the prod_idx counter to 1 (and the new_page), reserving the first
86 * slot (number '0') for us (reservation prevents DoS). */
87 my_slot = (uintptr_t)new_page;
88 atomic_set(&ucq->prod_idx, my_slot + 1);
89 /* Fallthrough to clear overflow and unlock */
91 /* Clear the overflow, so new producers will try to get a slot */
92 ucq->prod_overflow = FALSE;
93 /* At this point, any normal (non-locking) producers can succeed in getting
94 * a slot. The ones that failed earlier will fight for the lock, then
95 * quickly proceed when they get a good slot */
96 hash_unlock_irqsave(p->ucq_hashlock, (long)ucq);
97 /* Fall through to having a slot */
99 /* Sanity check on our slot. */
100 assert(slot_is_good(my_slot));
101 /* Convert slot to actual msg_container. Note we never actually deref
102 * my_slot here (o/w we'd need a rw_addr check). */
103 my_msg = slot2msg(my_slot);
104 /* Make sure our msg is user RW */
105 if (!is_user_rwaddr(my_msg, sizeof(struct msg_container)))
107 /* Finally write the message */
108 my_msg->ev_msg = *msg;
110 /* Now that the write is done, signal to the consumer that they can consume
111 * our message (they could have been spinning on it) */
112 my_msg->ready = TRUE;
115 /* Had a bad addr while holding the lock. This is a bit more serious */
116 warn("Bad addr in ucq page management!");
117 ucq->prod_overflow = FALSE;
118 hash_unlock_irqsave(p->ucq_hashlock, (long)ucq);
119 /* Fall-through to normal error out */
121 warn("Invalid user address, not sending a message");
122 /* TODO: consider killing the process here. For now, just catch it. For
123 * some cases, we have a slot that we never fill in, though if we had a bad
124 * addr, none of this will work out and the kernel just needs to protect
133 /* Prints the status and up to 25 of the previous messages for the UCQ. */
134 void print_ucq(struct proc *p, struct ucq *ucq)
136 struct ucq_page *ucq_pg;
137 struct proc *old_proc = switch_to(p);
139 printk("UCQ %p\n", ucq);
140 printk("prod_idx: %p, cons_idx: %p\n", atomic_read(&ucq->prod_idx),
141 atomic_read(&ucq->cons_idx));
142 printk("spare_pg: %p, nr_extra_pgs: %d\n", atomic_read(&ucq->spare_pg),
143 atomic_read(&ucq->nr_extra_pgs));
144 printk("prod_overflow: %d\n", ucq->prod_overflow);
145 /* Try to see our previous ucqs */
146 for (int i = atomic_read(&ucq->prod_idx), count = 0;
147 slot_is_good(i), count < 25; i--, count++) {
148 /* only attempt to print messages on the same page */
149 if (PTE_ADDR(i) != PTE_ADDR(atomic_read(&ucq->prod_idx)))
151 printk("Prod idx %p message ready is %p\n", i, slot2msg(i)->ready);
153 /* look at the chain, starting from cons_idx */
154 ucq_pg = (struct ucq_page*)PTE_ADDR(atomic_read(&ucq->cons_idx));
155 for (int i = 0; i < 10 && ucq_pg; i++) {
156 printk("#%02d: Cons page: %p, nr_cons: %d, next page: %p\n", i,
157 ucq_pg, ucq_pg->header.nr_cons, ucq_pg->header.cons_next_pg);
158 ucq_pg = (struct ucq_page*)(ucq_pg->header.cons_next_pg);
160 switch_back(p, old_proc);