FreeBSD/sys/kern/subr_rman.c
/* Shrink or extend one or both ends of an allocated resource. */
Shrink or extend one or both ends of an allocated resource.
Shrink or extend one or both ends of an allocated resource .
int
rman_adjust_resource(struct resource *rr, rman_res_t start, rman_res_t end)
{
	struct resource_i *r, *s, *t, *new;
	struct rman *rm;
	r = rr->__r_i;
	if (r->r_flags & RF_SHAREABLE)
		return (EINVAL);
	if (end < r->r_start || r->r_end < start)
		return (EINVAL);
	rm = r->r_rm;
	mtx_lock(rm->rm_mtx);
#ifdef INVARIANTS
	TAILQ_FOREACH(s, &rm->rm_list, r_link) {
		if (s == r)
			break;
	}
	if (s == NULL)
		panic("resource not in list");
#endif
	s = TAILQ_PREV(r, resource_head, r_link);
	t = TAILQ_NEXT(r, r_link);
	KASSERT(s == NULL || s->r_end + 1 == r->r_start,
	    ("prev resource mismatch"));
	KASSERT(t == NULL || r->r_end + 1 == t->r_start,
	    ("next resource mismatch"));
	if (start < r->r_start && (s == NULL || (s->r_flags & RF_ALLOCATED) ||
	    s->r_start > start)) {
		mtx_unlock(rm->rm_mtx);
		return (EBUSY);
	}
	if (end > r->r_end && (t == NULL || (t->r_flags & RF_ALLOCATED) ||
	    t->r_end < end)) {
		mtx_unlock(rm->rm_mtx);
		return (EBUSY);
	}
	if (start < r->r_start ||
	    (start > r->r_start && s != NULL && !(s->r_flags & RF_ALLOCATED))) {
		KASSERT(s->r_flags == 0, ("prev is busy"));
		r->r_start = start;
		if (s->r_start == start) {
			TAILQ_REMOVE(&rm->rm_list, s, r_link);
			free(s, M_RMAN);
		} else
			s->r_end = start - 1;
	}
	if (end > r->r_end ||
	    (end < r->r_end && t != NULL && !(t->r_flags & RF_ALLOCATED))) {
		KASSERT(t->r_flags == 0, ("next is busy"));
		r->r_end = end;
		if (t->r_end == end) {
			TAILQ_REMOVE(&rm->rm_list, t, r_link);
			free(t, M_RMAN);
		} else
			t->r_start = end + 1;
	}
	mtx_unlock(rm->rm_mtx);
	if (start > r->r_start) {
		new = int_alloc_resource(M_WAITOK);
		new->r_start = r->r_start;
		new->r_end = start - 1;
		new->r_rm = rm;
		mtx_lock(rm->rm_mtx);
		r->r_start = start;
		s = TAILQ_PREV(r, resource_head, r_link);
		if (s != NULL && !(s->r_flags & RF_ALLOCATED)) {
			s->r_end = start - 1;
			free(new, M_RMAN);
		} else
			TAILQ_INSERT_BEFORE(r, new, r_link);
		mtx_unlock(rm->rm_mtx);
	}
	if (end < r->r_end) {
		new = int_alloc_resource(M_WAITOK);
		new->r_start = end + 1;
		new->r_end = r->r_end;
		new->r_rm = rm;
		mtx_lock(rm->rm_mtx);
		r->r_end = end;
		t = TAILQ_NEXT(r, r_link);
		if (t != NULL && !(t->r_flags & RF_ALLOCATED)) {
			t->r_start = end + 1;
			free(new, M_RMAN);
		} else
			TAILQ_INSERT_AFTER(&rm->rm_list, r, new, r_link);
		mtx_unlock(rm->rm_mtx);
	}
	return (0);
}
int rman_adjust_resource ( struct resource * rr , rman_res_t start , rman_res_t end ) { struct resource_i * r , * s , * t , * new ; struct rman * rm ; r = rr -> __r_i ; if ( r -> r_flags & RF_SHAREABLE ) return ( EINVAL ) ; if ( end < r -> r_start || r -> r_end < start ) return ( EINVAL ) ; rm = r -> r_rm ; mtx_lock ( rm -> rm_mtx ) ; #ifdef INVARIANTS TAILQ_FOREACH ( s , & rm -> rm_list , r_link ) { if ( s == r ) break ; } if ( s == NULL ) panic ( " " ) ; #endif s = TAILQ_PREV ( r , resource_head , r_link ) ; t = TAILQ_NEXT ( r , r_link ) ; KASSERT ( s == NULL || s -> r_end + 1 == r -> r_start , ( " " ) ) ; KASSERT ( t == NULL || r -> r_end + 1 == t -> r_start , ( " " ) ) ; if ( start < r -> r_start && ( s == NULL || ( s -> r_flags & RF_ALLOCATED ) || s -> r_start > start ) ) { mtx_unlock ( rm -> rm_mtx ) ; return ( EBUSY ) ; } if ( end > r -> r_end && ( t == NULL || ( t -> r_flags & RF_ALLOCATED ) || t -> r_end < end ) ) { mtx_unlock ( rm -> rm_mtx ) ; return ( EBUSY ) ; } if ( start < r -> r_start || ( start > r -> r_start && s != NULL && ! ( s -> r_flags & RF_ALLOCATED ) ) ) { KASSERT ( s -> r_flags == 0 , ( " " ) ) ; r -> r_start = start ; if ( s -> r_start == start ) { TAILQ_REMOVE ( & rm -> rm_list , s , r_link ) ; free ( s , M_RMAN ) ; } else s -> r_end = start - 1 ; } if ( end > r -> r_end || ( end < r -> r_end && t != NULL && ! ( t -> r_flags & RF_ALLOCATED ) ) ) { KASSERT ( t -> r_flags == 0 , ( " " ) ) ; r -> r_end = end ; if ( t -> r_end == end ) { TAILQ_REMOVE ( & rm -> rm_list , t , r_link ) ; free ( t , M_RMAN ) ; } else t -> r_start = end + 1 ; } mtx_unlock ( rm -> rm_mtx ) ; if ( start > r -> r_start ) { new = int_alloc_resource ( M_WAITOK ) ; new -> r_start = r -> r_start ; new -> r_end = start - 1 ; new -> r_rm = rm ; mtx_lock ( rm -> rm_mtx ) ; r -> r_start = start ; s = TAILQ_PREV ( r , resource_head , r_link ) ; if ( s != NULL && ! ( s -> r_flags & RF_ALLOCATED ) ) { s -> r_end = start - 1 ; free ( new , M_RMAN ) ; } else TAILQ_INSERT_BEFORE ( r , new , r_link ) ; mtx_unlock ( rm -> rm_mtx ) ; } if ( end < r -> r_end ) { new = int_alloc_resource ( M_WAITOK ) ; new -> r_start = end + 1 ; new -> r_end = r -> r_end ; new -> r_rm = rm ; mtx_lock ( rm -> rm_mtx ) ; r -> r_end = end ; t = TAILQ_NEXT ( r , r_link ) ; if ( t != NULL && ! ( t -> r_flags & RF_ALLOCATED ) ) { t -> r_start = end + 1 ; free ( new , M_RMAN ) ; } else TAILQ_INSERT_AFTER ( & rm -> rm_list , r , new , r_link ) ; mtx_unlock ( rm -> rm_mtx ) ; } return ( 0 ) ; }
Shrink or extend one or both ends of an allocated resource.
Shrink or extend one or both ends of an allocated resource .
/* Not supported for shared resources. */ /*
	 * This does not support wholesale moving of a resource.  At
	 * least part of the desired new range must overlap with the
	 * existing resource.
	 */ /*
	 * Find the two resource regions immediately adjacent to the
	 * allocated resource.
	 */ /*
	 * See if the changes are permitted.  Shrinking is always allowed,
	 * but growing requires sufficient room in the adjacent region.
	 */ /*
	 * While holding the lock, grow either end of the resource as
	 * needed and shrink either end if the shrinking does not require
	 * allocating a new resource.  We can safely drop the lock and then
	 * insert a new range to handle the shrinking case afterwards.
	 */ /*
	 * Handle the shrinking cases that require allocating a new
	 * resource to hold the newly-free region.  We have to recheck
	 * if we still need this new region after acquiring the lock.
	 */