|
Lines 2215-2220
unp_externalize_fp(struct file *fp)
Link Here
|
| 2215 |
return (ret); |
2215 |
return (ret); |
| 2216 |
} |
2216 |
} |
| 2217 |
|
2217 |
|
|
|
2218 |
static void |
| 2219 |
unp_gc_scan(struct unpcb *unp, void (*op)(struct filedescent **, int)) |
| 2220 |
{ |
| 2221 |
struct socket *so, *soa; |
| 2222 |
|
| 2223 |
so = unp->unp_socket; |
| 2224 |
SOCK_LOCK(so); |
| 2225 |
if (SOLISTENING(so)) { |
| 2226 |
/* |
| 2227 |
* Mark all sockets in our accept queue. |
| 2228 |
*/ |
| 2229 |
TAILQ_FOREACH(soa, &so->sol_comp, so_list) { |
| 2230 |
if (sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) |
| 2231 |
continue; |
| 2232 |
SOCKBUF_LOCK(&soa->so_rcv); |
| 2233 |
unp_scan(soa->so_rcv.sb_mb, op); |
| 2234 |
SOCKBUF_UNLOCK(&soa->so_rcv); |
| 2235 |
} |
| 2236 |
} else { |
| 2237 |
/* |
| 2238 |
* Mark all sockets we reference with RIGHTS. |
| 2239 |
*/ |
| 2240 |
if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { |
| 2241 |
SOCKBUF_LOCK(&so->so_rcv); |
| 2242 |
unp_scan(so->so_rcv.sb_mb, op); |
| 2243 |
SOCKBUF_UNLOCK(&so->so_rcv); |
| 2244 |
} |
| 2245 |
} |
| 2246 |
SOCK_UNLOCK(so); |
| 2247 |
} |
| 2248 |
|
| 2218 |
/* |
2249 |
/* |
| 2219 |
* unp_defer indicates whether additional work has been defered for a future |
2250 |
* unp_defer indicates whether additional work has been defered for a future |
| 2220 |
* pass through unp_gc(). It is thread local and does not require explicit |
2251 |
* pass through unp_gc(). It is thread local and does not require explicit |
|
Lines 2223-2230
unp_externalize_fp(struct file *fp)
Link Here
|
| 2223 |
static int unp_marked; |
2254 |
static int unp_marked; |
| 2224 |
static int unp_unreachable; |
2255 |
static int unp_unreachable; |
| 2225 |
|
2256 |
|
|
|
2257 |
/* |
| 2258 |
* The cycle detection algorithm may only operate on sockets that have been |
| 2259 |
* marked as UNPGC_DEAD at the start of unp_gc(). However, the UNPGC_DEAD flag |
| 2260 |
* is cleared if it turns out the socket is not in a cycle after all. In |
| 2261 |
* this case there is the UNPGC_SCANNED flag set, though. This is the reason |
| 2262 |
* unp_decrease_msgcount/unp_increase_msgcount check for (UNPGC_DEAD | |
| 2263 |
* UNPGC_SCANNED). |
| 2264 |
*/ |
| 2265 |
|
| 2266 |
#ifdef INVARIANTS |
| 2267 |
static int msgcount_changed; |
| 2268 |
#endif |
| 2269 |
|
| 2226 |
static void |
2270 |
static void |
| 2227 |
unp_accessable(struct filedescent **fdep, int fdcount) |
2271 |
unp_decrease_msgcount(struct filedescent **fdep, int fdcount) |
| 2228 |
{ |
2272 |
{ |
| 2229 |
struct unpcb *unp; |
2273 |
struct unpcb *unp; |
| 2230 |
struct file *fp; |
2274 |
struct file *fp; |
|
Lines 2234-2243
unp_accessable(struct filedescent **fdep, int fdcount)
Link Here
|
| 2234 |
fp = fdep[i]->fde_file; |
2278 |
fp = fdep[i]->fde_file; |
| 2235 |
if ((unp = fptounp(fp)) == NULL) |
2279 |
if ((unp = fptounp(fp)) == NULL) |
| 2236 |
continue; |
2280 |
continue; |
| 2237 |
if (unp->unp_gcflag & UNPGC_REF) |
2281 |
if (!(unp->unp_gcflag & (UNPGC_DEAD | UNPGC_SCANNED))) |
| 2238 |
continue; |
2282 |
continue; |
| 2239 |
unp->unp_gcflag &= ~UNPGC_DEAD; |
2283 |
unp->unp_msgcount--; |
| 2240 |
unp->unp_gcflag |= UNPGC_REF; |
2284 |
#ifdef INVARIANTS |
|
|
2285 |
msgcount_changed--; |
| 2286 |
#endif |
| 2287 |
} |
| 2288 |
} |
| 2289 |
|
| 2290 |
static void |
| 2291 |
unp_increase_msgcount(struct filedescent **fdep, int fdcount) |
| 2292 |
{ |
| 2293 |
struct unpcb *unp; |
| 2294 |
struct file *fp; |
| 2295 |
int i; |
| 2296 |
|
| 2297 |
for (i = 0; i < fdcount; i++) { |
| 2298 |
fp = fdep[i]->fde_file; |
| 2299 |
if ((unp = fptounp(fp)) == NULL) |
| 2300 |
continue; |
| 2301 |
if (!(unp->unp_gcflag & (UNPGC_DEAD | UNPGC_SCANNED))) |
| 2302 |
continue; |
| 2303 |
unp->unp_msgcount++; |
| 2304 |
#ifdef INVARIANTS |
| 2305 |
msgcount_changed++; |
| 2306 |
#endif |
| 2241 |
unp_marked++; |
2307 |
unp_marked++; |
| 2242 |
} |
2308 |
} |
| 2243 |
} |
2309 |
} |
|
Lines 2245-2295
unp_accessable(struct filedescent **fdep, int fdcount)
Link Here
|
| 2245 |
static void |
2311 |
static void |
| 2246 |
unp_gc_process(struct unpcb *unp) |
2312 |
unp_gc_process(struct unpcb *unp) |
| 2247 |
{ |
2313 |
{ |
| 2248 |
struct socket *so, *soa; |
2314 |
|
| 2249 |
struct file *fp; |
2315 |
/* Not potentially in a cycle. Nothing to do. */ |
|
|
2316 |
if (!(unp->unp_gcflag & UNPGC_DEAD)) |
| 2317 |
return; |
| 2250 |
|
2318 |
|
| 2251 |
/* Already processed. */ |
2319 |
/* Already processed. */ |
| 2252 |
if (unp->unp_gcflag & UNPGC_SCANNED) |
2320 |
if (unp->unp_gcflag & UNPGC_SCANNED) |
| 2253 |
return; |
2321 |
return; |
| 2254 |
fp = unp->unp_file; |
|
|
| 2255 |
|
2322 |
|
| 2256 |
/* |
2323 |
unp->unp_gcflag |= UNPGC_SCANNED; |
| 2257 |
* Check for a socket potentially in a cycle. It must be in a |
|
|
| 2258 |
* queue as indicated by msgcount, and this must equal the file |
| 2259 |
* reference count. Note that when msgcount is 0 the file is NULL. |
| 2260 |
*/ |
| 2261 |
if ((unp->unp_gcflag & UNPGC_REF) == 0 && fp && |
| 2262 |
unp->unp_msgcount != 0 && fp->f_count == unp->unp_msgcount) { |
| 2263 |
unp->unp_gcflag |= UNPGC_DEAD; |
| 2264 |
unp_unreachable++; |
| 2265 |
return; |
| 2266 |
} |
| 2267 |
|
2324 |
|
| 2268 |
so = unp->unp_socket; |
2325 |
if (unp->unp_msgcount > 0) { |
| 2269 |
SOCK_LOCK(so); |
2326 |
unp->unp_gcflag &= ~UNPGC_DEAD; |
| 2270 |
if (SOLISTENING(so)) { |
2327 |
unp_unreachable--; |
| 2271 |
/* |
2328 |
unp_gc_scan(unp, unp_increase_msgcount); |
| 2272 |
* Mark all sockets in our accept queue. |
|
|
| 2273 |
*/ |
| 2274 |
TAILQ_FOREACH(soa, &so->sol_comp, so_list) { |
| 2275 |
if (sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) |
| 2276 |
continue; |
| 2277 |
SOCKBUF_LOCK(&soa->so_rcv); |
| 2278 |
unp_scan(soa->so_rcv.sb_mb, unp_accessable); |
| 2279 |
SOCKBUF_UNLOCK(&soa->so_rcv); |
| 2280 |
} |
| 2281 |
} else { |
| 2282 |
/* |
| 2283 |
* Mark all sockets we reference with RIGHTS. |
| 2284 |
*/ |
| 2285 |
if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { |
| 2286 |
SOCKBUF_LOCK(&so->so_rcv); |
| 2287 |
unp_scan(so->so_rcv.sb_mb, unp_accessable); |
| 2288 |
SOCKBUF_UNLOCK(&so->so_rcv); |
| 2289 |
} |
| 2290 |
} |
2329 |
} |
| 2291 |
SOCK_UNLOCK(so); |
|
|
| 2292 |
unp->unp_gcflag |= UNPGC_SCANNED; |
| 2293 |
} |
2330 |
} |
| 2294 |
|
2331 |
|
| 2295 |
static int unp_recycled; |
2332 |
static int unp_recycled; |
|
Lines 2321-2354
unp_gc(__unused void *arg, int pending)
Link Here
|
| 2321 |
unp->unp_gcflag = |
2358 |
unp->unp_gcflag = |
| 2322 |
(unp->unp_gcflag & UNPGC_IGNORE_RIGHTS); |
2359 |
(unp->unp_gcflag & UNPGC_IGNORE_RIGHTS); |
| 2323 |
|
2360 |
|
|
|
2361 |
unp_unreachable = 0; |
| 2362 |
|
| 2363 |
for (head = heads; *head != NULL; head++) |
| 2364 |
LIST_FOREACH(unp, *head, unp_link) { |
| 2365 |
struct file *fp; |
| 2366 |
|
| 2367 |
fp = unp->unp_file; |
| 2368 |
|
| 2369 |
/* |
| 2370 |
* Check for a socket potentially in a cycle. It must |
| 2371 |
* be in a queue as indicated by msgcount, and this |
| 2372 |
* must equal the file reference count. Note that when |
| 2373 |
* msgcount is 0 the file is NULL. |
| 2374 |
*/ |
| 2375 |
if (fp && unp->unp_msgcount != 0 && |
| 2376 |
fp->f_count == unp->unp_msgcount) { |
| 2377 |
unp->unp_gcflag |= UNPGC_DEAD; |
| 2378 |
++unp_unreachable; |
| 2379 |
} |
| 2380 |
} |
| 2381 |
|
| 2382 |
/* Only one potentially unreachable socket cannot form a cycle. */ |
| 2383 |
if (unp_unreachable <= 1) { |
| 2384 |
UNP_LINK_RUNLOCK(); |
| 2385 |
return; |
| 2386 |
} |
| 2387 |
|
| 2388 |
for (head = heads; *head != NULL; head++) |
| 2389 |
LIST_FOREACH(unp, *head, unp_link) { |
| 2390 |
if (unp->unp_gcflag & UNPGC_DEAD) { |
| 2391 |
unp_gc_scan(unp, unp_decrease_msgcount); |
| 2392 |
} |
| 2393 |
} |
| 2394 |
|
| 2324 |
/* |
2395 |
/* |
| 2325 |
* Scan marking all reachable sockets with UNPGC_REF. Once a socket |
2396 |
* If a socket still has a non-negative msgcount it cannot be in a |
| 2326 |
* is reachable all of the sockets it references are reachable. |
2397 |
* cycle. In this case increment msgcount of all children iteratively. |
| 2327 |
* Stop the scan once we do a complete loop without discovering |
2398 |
* Stop the scan once we do a complete loop without discovering |
| 2328 |
* a new reachable socket. |
2399 |
* a new reachable socket. |
| 2329 |
*/ |
2400 |
*/ |
| 2330 |
do { |
2401 |
do { |
| 2331 |
unp_unreachable = 0; |
|
|
| 2332 |
unp_marked = 0; |
2402 |
unp_marked = 0; |
| 2333 |
for (head = heads; *head != NULL; head++) |
2403 |
for (head = heads; *head != NULL; head++) |
| 2334 |
LIST_FOREACH(unp, *head, unp_link) |
2404 |
LIST_FOREACH(unp, *head, unp_link) |
| 2335 |
unp_gc_process(unp); |
2405 |
unp_gc_process(unp); |
| 2336 |
} while (unp_marked); |
2406 |
} while (unp_marked); |
| 2337 |
UNP_LINK_RUNLOCK(); |
2407 |
|
| 2338 |
if (unp_unreachable == 0) |
2408 |
/* |
| 2339 |
return; |
2409 |
* Sockets that still have the UNPGC_DEAD flag set are unreachable. |
|
|
2410 |
* Restore msgcount of their children, too, so that no msgcount field |
| 2411 |
* is changed by the algorithm. |
| 2412 |
*/ |
| 2413 |
for (head = heads; *head != NULL; head++) |
| 2414 |
LIST_FOREACH(unp, *head, unp_link) { |
| 2415 |
if (unp->unp_gcflag & UNPGC_DEAD) { |
| 2416 |
unp_gc_scan(unp, unp_increase_msgcount); |
| 2417 |
} |
| 2418 |
} |
| 2419 |
|
| 2420 |
KASSERT(msgcount_changed == 0, |
| 2421 |
("unp_gc: msgcount fields must not be changed")); |
| 2340 |
|
2422 |
|
| 2341 |
/* |
2423 |
/* |
| 2342 |
* Allocate space for a local list of dead unpcbs. |
2424 |
* Allocate space for a local list of dead unpcbs. |
|
|
2425 |
* unp_unreachable is the number of sockets that have |
| 2426 |
* the UNPGC_DEAD flag set. |
| 2343 |
*/ |
2427 |
*/ |
| 2344 |
unref = malloc(unp_unreachable * sizeof(struct file *), |
2428 |
unref = malloc(unp_unreachable * sizeof(struct file *), |
| 2345 |
M_TEMP, M_WAITOK); |
2429 |
M_TEMP, M_WAITOK); |
| 2346 |
|
2430 |
|
| 2347 |
/* |
2431 |
/* |
| 2348 |
* Iterate looking for sockets which have been specifically marked |
2432 |
* Iterate looking for sockets which have been specifically marked |
| 2349 |
* as as unreachable and store them locally. |
2433 |
* as unreachable and store them locally. |
| 2350 |
*/ |
2434 |
*/ |
| 2351 |
UNP_LINK_RLOCK(); |
|
|
| 2352 |
for (total = 0, head = heads; *head != NULL; head++) |
2435 |
for (total = 0, head = heads; *head != NULL; head++) |
| 2353 |
LIST_FOREACH(unp, *head, unp_link) |
2436 |
LIST_FOREACH(unp, *head, unp_link) |
| 2354 |
if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { |
2437 |
if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { |