FreeBSD/sys/geom/vinum/geom_vinum_subr.c
/*
 * Give a subdisk to a drive, check and adjust several parameters, adjust
 * freelist.
 */
Give a subdisk to a drive, check and adjust several parameters, adjust
freelist.
Give a subdisk to a drive check and adjust several parameters adjust freelist .
int
gv_sd_to_drive(struct gv_sd *s, struct gv_drive *d)
{
	struct gv_sd *s2;
	struct gv_freelist *fl, *fl2;
	off_t tmp;
	int i;
	fl2 = NULL;
	if (d->flags & GV_DRIVE_REFERENCED) {
		s->drive_sc = d;
		return (0);
	}
	if (s->drive_sc != NULL) {
		if (s->drive_sc == d) {
			if (!(s->flags & GV_SD_TASTED)) {
				return (0);
			}
		} else {
			G_VINUM_DEBUG(0, "error giving subdisk '%s' to '%s' "
			    "(already on '%s')", s->name, d->name,
			    s->drive_sc->name);
			return (GV_ERR_ISATTACHED);
		}
	}
	if ((s->size > d->avail) || (d->freelist_entries == 0)) {
		G_VINUM_DEBUG(0, "not enough space on '%s' for '%s'", d->name,
		    s->name);
		return (GV_ERR_NOSPACE);
	}
	if (s->size == -1) {
		LIST_FOREACH(fl, &d->freelist, freelist) {
			if (fl->size < s->size)
				continue;
			s->size = fl->size;
			s->drive_offset = fl->offset;
			fl2 = fl;
		}
		if (s->size == -1) {
			G_VINUM_DEBUG(0, "unable to autosize '%s' on '%s'",
			    s->name, d->name);
			return (GV_ERR_BADSIZE);
		}
	} else {
		i = 0;
		LIST_FOREACH(fl, &d->freelist, freelist) {
			if (fl->size < s->size)
				continue;
			if (s->drive_offset == -1)
				s->drive_offset = fl->offset;
			fl2 = fl;
			i++;
			break;
		}
		if (i == 0) {
			G_VINUM_DEBUG(0, "free slots to small for '%s' on '%s'",
			    s->name, d->name);
			return (GV_ERR_NOSPACE);
		}
	}
	if (s->drive_offset == -1) {
		LIST_FOREACH(s2, &d->subdisks, from_drive) {
			s->drive_offset = s2->drive_offset + s2->size;
		}
		if (s->drive_offset == -1)
			s->drive_offset = GV_DATA_START;
	} else {
		i = 0;
		LIST_FOREACH(fl, &d->freelist, freelist) {
			if ((fl->offset <= s->drive_offset) &&
			    (fl->offset + fl->size >=
			    s->drive_offset + s->size)) {
				i++;
				fl2 = fl;
				break;
			}
		}
		if (i == 0) {
			G_VINUM_DEBUG(0, "given drive_offset for '%s' won't fit "
			    "on '%s'", s->name, d->name);
			return (GV_ERR_NOSPACE);
		}
	}
	LIST_FOREACH(fl, &d->freelist, freelist) {
		if (fl != fl2)
			continue;
		if (fl->offset == s->drive_offset) {
			fl->offset += s->size;
			fl->size -= s->size;
			if (fl->size == 0) {
				d->freelist_entries--;
				LIST_REMOVE(fl, freelist);
			}
		} else {
			tmp = fl->offset + fl->size;
			fl->size = s->drive_offset - fl->offset;
			if (s->drive_offset + s->size != tmp) {
				fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO);
				fl2->offset = s->drive_offset + s->size;
				fl2->size = tmp - fl2->offset;
				LIST_INSERT_AFTER(fl, fl2, freelist);
				d->freelist_entries++;
			}
		}
		break;
	}
	if (LIST_EMPTY(&d->subdisks)) {
		LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
	} else {
		LIST_FOREACH(s2, &d->subdisks, from_drive) {
			if (s->drive_offset < s2->drive_offset) {
				LIST_INSERT_BEFORE(s2, s, from_drive);
				break;
			} else if (LIST_NEXT(s2, from_drive) == NULL) {
				LIST_INSERT_AFTER(s2, s, from_drive);
				break;
			}
		}
	}
	d->sdcount++;
	d->avail -= s->size;
	s->flags &= ~GV_SD_TASTED;
	s->drive_sc = d;
	return (0);
}
int gv_sd_to_drive ( struct gv_sd * s , struct gv_drive * d ) { struct gv_sd * s2 ; struct gv_freelist * fl , * fl2 ; off_t tmp ; int i ; fl2 = NULL ; if ( d -> flags & GV_DRIVE_REFERENCED ) { s -> drive_sc = d ; return ( 0 ) ; } if ( s -> drive_sc != NULL ) { if ( s -> drive_sc == d ) { if ( ! ( s -> flags & GV_SD_TASTED ) ) { return ( 0 ) ; } } else { G_VINUM_DEBUG ( 0 , " " " " , s -> name , d -> name , s -> drive_sc -> name ) ; return ( GV_ERR_ISATTACHED ) ; } } if ( ( s -> size > d -> avail ) || ( d -> freelist_entries == 0 ) ) { G_VINUM_DEBUG ( 0 , " " , d -> name , s -> name ) ; return ( GV_ERR_NOSPACE ) ; } if ( s -> size == -1 ) { LIST_FOREACH ( fl , & d -> freelist , freelist ) { if ( fl -> size < s -> size ) continue ; s -> size = fl -> size ; s -> drive_offset = fl -> offset ; fl2 = fl ; } if ( s -> size == -1 ) { G_VINUM_DEBUG ( 0 , " " , s -> name , d -> name ) ; return ( GV_ERR_BADSIZE ) ; } } else { i = 0 ; LIST_FOREACH ( fl , & d -> freelist , freelist ) { if ( fl -> size < s -> size ) continue ; if ( s -> drive_offset == -1 ) s -> drive_offset = fl -> offset ; fl2 = fl ; i ++ ; break ; } if ( i == 0 ) { G_VINUM_DEBUG ( 0 , " " , s -> name , d -> name ) ; return ( GV_ERR_NOSPACE ) ; } } if ( s -> drive_offset == -1 ) { LIST_FOREACH ( s2 , & d -> subdisks , from_drive ) { s -> drive_offset = s2 -> drive_offset + s2 -> size ; } if ( s -> drive_offset == -1 ) s -> drive_offset = GV_DATA_START ; } else { i = 0 ; LIST_FOREACH ( fl , & d -> freelist , freelist ) { if ( ( fl -> offset <= s -> drive_offset ) && ( fl -> offset + fl -> size >= s -> drive_offset + s -> size ) ) { i ++ ; fl2 = fl ; break ; } } if ( i == 0 ) { G_VINUM_DEBUG ( 0 , " " " " , s -> name , d -> name ) ; return ( GV_ERR_NOSPACE ) ; } } LIST_FOREACH ( fl , & d -> freelist , freelist ) { if ( fl != fl2 ) continue ; if ( fl -> offset == s -> drive_offset ) { fl -> offset += s -> size ; fl -> size -= s -> size ; if ( fl -> size == 0 ) { d -> freelist_entries -- ; LIST_REMOVE ( fl , freelist ) ; } } else { tmp = fl -> offset + fl -> size ; fl -> size = s -> drive_offset - fl -> offset ; if ( s -> drive_offset + s -> size != tmp ) { fl2 = g_malloc ( sizeof ( * fl2 ) , M_WAITOK | M_ZERO ) ; fl2 -> offset = s -> drive_offset + s -> size ; fl2 -> size = tmp - fl2 -> offset ; LIST_INSERT_AFTER ( fl , fl2 , freelist ) ; d -> freelist_entries ++ ; } } break ; } if ( LIST_EMPTY ( & d -> subdisks ) ) { LIST_INSERT_HEAD ( & d -> subdisks , s , from_drive ) ; } else { LIST_FOREACH ( s2 , & d -> subdisks , from_drive ) { if ( s -> drive_offset < s2 -> drive_offset ) { LIST_INSERT_BEFORE ( s2 , s , from_drive ) ; break ; } else if ( LIST_NEXT ( s2 , from_drive ) == NULL ) { LIST_INSERT_AFTER ( s2 , s , from_drive ) ; break ; } } } d -> sdcount ++ ; d -> avail -= s -> size ; s -> flags &= ~ GV_SD_TASTED ; s -> drive_sc = d ; return ( 0 ) ; }
Give a subdisk to a drive, check and adjust several parameters, adjust
 freelist.
Give a subdisk to a drive check and adjust several parameters adjust freelist .
/* Shortcut for "referenced" drives. */ /* Check if this subdisk was already given to this drive. */ /* Preliminary checks. */ /* If no size was given for this subdisk, try to auto-size it... */ /* Find the largest available slot. */ /* No good slot found? */ /*
	 * ... or check if we have a free slot that's large enough for the
	 * given size.
	 */ /* Assign drive offset, if not given. */ /* Couldn't find a good free slot. */ /* No drive offset given, try to calculate it. */ /* Add offsets and sizes from other subdisks on this drive. */ /*
		 * If there are no other subdisks yet, then set the default
		 * offset to GV_DATA_START.
		 */ /* Check if we have a free slot at the given drive offset. */ /* Yes, this subdisk fits. */ /* Couldn't find a good free slot. */ /*
	 * Now that all parameters are checked and set up, we can give the
	 * subdisk to the drive and adjust the freelist.
	 */ /* First, adjust the freelist. */ /* Look for the free slot that we have found before. */ /* The subdisk starts at the beginning of the free slot. */ /* The subdisk uses the whole slot, so remove it. */ /*
		 * The subdisk does not start at the beginning of the free
		 * slot.
		 */ /*
			 * The subdisk didn't use the complete rest of the free
			 * slot, so we need to split it.
			 */ /*
	 * This is the first subdisk on this drive, just insert it into the
	 * list.
	 */ /* There are other subdisks, so insert this one in correct order. */ /* Link back from the subdisk to this drive. */