View | Details | Raw Unified | Return to bug 227285
Collapse All | Expand All

(-)b/sys/kern/uipc_usrreq.c (-50 / +133 lines)
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) {
(-)b/sys/sys/unpcb.h (-4 / +3 lines)
Lines 111-120 struct unpcb { Link Here
111
/*
111
/*
112
 * Flags in unp_gcflag.
112
 * Flags in unp_gcflag.
113
 */
113
 */
114
#define	UNPGC_REF			0x1	/* unpcb has external ref. */
114
#define	UNPGC_DEAD			0x1	/* unpcb might be dead. */
115
#define	UNPGC_DEAD			0x2	/* unpcb might be dead. */
115
#define	UNPGC_SCANNED			0x2	/* Has been scanned. */
116
#define	UNPGC_SCANNED			0x4	/* Has been scanned. */
116
#define	UNPGC_IGNORE_RIGHTS		0x4	/* Attached rights are freed */
117
#define	UNPGC_IGNORE_RIGHTS		0x8	/* Attached rights are freed */
118
117
119
#define	sotounpcb(so)	((struct unpcb *)((so)->so_pcb))
118
#define	sotounpcb(so)	((struct unpcb *)((so)->so_pcb))
120
119

Return to bug 227285