diff -Naur lilo-21.7.5/geometry.c lilo-21.7.5a/geometry.c --- lilo-21.7.5/geometry.c Tue Apr 17 15:38:21 2001 +++ lilo-21.7.5a/geometry.c Tue Aug 28 10:29:45 2001 @@ -39,6 +39,18 @@ #endif #endif +struct lv_bmap { + __u32 lv_block; + __u16 lv_dev; +}; + +#ifndef LV_BMAP +#define LV_BMAP _IOWR(0xfe, 0x30, 1) +#endif +#ifndef LVM_GET_IOP_VERSION +#define LVM_GET_IOP_VERSION _IOR(0xfe, 0x98, 1) +#endif + #ifndef HDIO_GETGEO #define HDIO_GETGEO HDIO_REQ #endif @@ -278,6 +290,40 @@ } +void lvm_bmap(struct lv_bmap *lbm) +{ + DEVICE dev; + static int lvmfd = -1; + static dev_t last_dev = 0; + + if (lbm->lv_dev != last_dev) { + char lvm_char[] = "/dev/lvm"; + unsigned short iop; + + if (lvmfd != -1) + close(lvmfd); + + if ((lvmfd = open(lvm_char, lbm->lv_dev, O_RDONLY)) < 0) + die("can't open LVM char device %s\n", lvm_char); + + if (ioctl(lvmfd, LVM_GET_IOP_VERSION, &iop) < 0) + die("LVM_GET_IOP_VERSION failed on %s\n", lvm_char); + + if (iop < 10) + die("LVM IOP %d not supported for booting\n", iop); + close(lvmfd); + + lvmfd = dev_open(&dev, lbm->lv_dev, O_RDONLY); + if (lvmfd < 0) + die("can't open LVM block device %#x\n", lbm->lv_dev); + last_dev = lbm->lv_dev; + } + if (ioctl(lvmfd, LV_BMAP, lbm) < 0) { + perror(__FUNCTION__); + pdie("LV_BMAP error or ioctl unsupported, can't have image in LVM.\n"); + } +} + void geo_query_dev(GEOMETRY *geo,int device,int all) { DEVICE dev; @@ -468,6 +514,34 @@ DT_ENTRY *walk; int inherited,keep_cyls; + /* + * Find underlying device (PV) for LVM. It is OK if the underlying PV is + * really an MD RAID1 device, because the geometry of the RAID1 device is + * exactly the same as the underlying disk, so FIBMAP and LV_BMAP will + * return the correct block numbers regardless of MD (I think, not tested). + * + * We do a quick test to see if the LVM LV_BMAP ioctl is working correctly. + * It should map the two blocks with the same difference as they were input, + * with a constant offset from their original block numbers. If this is not + * the case then LV_BMAP is not working correctly (some widely distributed + * kernels did not have working LV_BMAP support, some just oops here). + */ + if (MAJOR(device) == MAJOR_LVM) + { + struct lv_bmap lbmA, lbmB; +#define DIFF 255 + + lbmA.lv_dev = lbmB.lv_dev = device; + lbmA.lv_block = 0; + lbmB.lv_block = DIFF; + + lvm_bmap(&lbmA); + lvm_bmap(&lbmB); + if (lbmB.lv_block - lbmA.lv_block != DIFF) + die("This version of LVM does not support boot LVs"); + device = geo->base_dev = lbmA.lv_dev; + } + /* Find underlying device for MD RAID */ if (MAJOR(device) == MD_MAJOR) { char mdxxx[16]; int md_fd, pass; @@ -574,7 +648,8 @@ if (fstat(geo->fd,&st) < 0) die("fstat %s: %s",name,strerror(errno)); if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode)) die("%s: neither a reg. file nor a block dev.",name); - geo_get(geo,S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev,user_dev,1); + geo->dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev; + geo_get(geo, geo->dev, user_dev, 1); geo->file = S_ISREG(st.st_mode); geo->boot = 0; #ifndef FIGETBSZ @@ -600,16 +675,15 @@ int geo_open_boot(GEOMETRY *geo,char *name) { struct stat st; - dev_t dev; if (stat(name,&st) < 0) die("stat %s: %s",name,strerror(errno)); if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode)) die("%s: neither a reg. file nor a block dev.",name); - dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev; - if (MAJOR(dev) == MAJOR_FD) geo->fd = 0; + geo->dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev; + if (MAJOR(geo->dev) == MAJOR_FD) geo->fd = 0; else if ((geo->fd = open(name,O_NOACCESS)) < 0) die("open %s: %s",name,strerror(errno)); - geo_get(geo,dev,-1,0); + geo_get(geo, geo->dev, -1, 0); geo->file = S_ISREG(st.st_mode); geo->boot = 1; geo->spb = 1; @@ -652,6 +726,17 @@ if (!block) { return 0; } + } + if (MAJOR(geo->dev) == MAJOR_LVM) { + struct lv_bmap lbm; + + lbm.lv_dev = geo->dev; + lbm.lv_block = block; + + lvm_bmap(&lbm); + if (lbm.lv_dev != geo->base_dev) + die("LVM boot LV cannot be on multiple PVs\n"); + block = lbm.lv_block; } sector = block*geo->spb+((offset/SECTOR_SIZE) % geo->spb); sector += geo->start; diff -Naur lilo-21.7.5/geometry.h lilo-21.7.5a/geometry.h --- lilo-21.7.5/geometry.h Thu Apr 5 22:49:26 2001 +++ lilo-21.7.5a/geometry.h Tue Aug 28 10:31:05 2001 @@ -16,6 +16,7 @@ int spb; /* sectors per block */ int fd,file; int boot; /* non-zero after geo_open_boot */ + dev_t dev, base_dev; /* real device if remapping (LVM, etc) */ } GEOMETRY; typedef struct _dt_entry { diff -Naur lilo-21.7.5/lilo.h lilo-21.7.5a/lilo.h --- lilo-21.7.5/lilo.h Tue Mar 20 15:30:29 2001 +++ lilo-21.7.5a/lilo.h Tue Aug 28 10:31:27 2001 @@ -44,6 +44,7 @@ #define MAJOR_DAC960 48 /* First Mylex DAC960 PCI RAID controller */ #define MAJOR_IDE5 56 /* IDE on fifth interface */ #define MAJOR_IDE6 57 /* IDE on sixth interface */ +#define MAJOR_LVM 58 /* Logical Volume Manager block device */ #define MAJOR_IDE7 88 /* IDE on seventh interface */ #define MAJOR_IDE8 89 /* IDE on eighth interface */ #define MAJOR_IDE9 90 /* IDE on ninth interface */