Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / exofs / ore.c
index ddbf872..1b8b446 100644 (file)
@@ -146,68 +146,82 @@ int  _ore_get_io_state(struct ore_layout *layout,
                        struct ore_io_state **pios)
 {
        struct ore_io_state *ios;
-       struct page **pages;
-       struct osd_sg_entry *sgilist;
+       size_t size_ios, size_extra, size_total;
+       void *ios_extra;
+
+       /*
+        * The desired layout looks like this, with the extra_allocation
+        * items pointed at from fields within ios or per_dev:
+
        struct __alloc_all_io_state {
                struct ore_io_state ios;
                struct ore_per_dev_state per_dev[numdevs];
                union {
                        struct osd_sg_entry sglist[sgs_per_dev * numdevs];
                        struct page *pages[num_par_pages];
-               };
-       } *_aios;
-
-       if (likely(sizeof(*_aios) <= PAGE_SIZE)) {
-               _aios = kzalloc(sizeof(*_aios), GFP_KERNEL);
-               if (unlikely(!_aios)) {
-                       ORE_DBGMSG("Failed kzalloc bytes=%zd\n",
-                                  sizeof(*_aios));
+               } extra_allocation;
+       } whole_allocation;
+
+       */
+
+       /* This should never happen, so abort early if it ever does. */
+       if (sgs_per_dev && num_par_pages) {
+               ORE_DBGMSG("Tried to use both pages and sglist\n");
+               *pios = NULL;
+               return -EINVAL;
+       }
+
+       if (numdevs > (INT_MAX - sizeof(*ios)) /
+                      sizeof(struct ore_per_dev_state))
+               return -ENOMEM;
+       size_ios = sizeof(*ios) + sizeof(struct ore_per_dev_state) * numdevs;
+
+       if (sgs_per_dev * numdevs > INT_MAX / sizeof(struct osd_sg_entry))
+               return -ENOMEM;
+       if (num_par_pages > INT_MAX / sizeof(struct page *))
+               return -ENOMEM;
+       size_extra = max(sizeof(struct osd_sg_entry) * (sgs_per_dev * numdevs),
+                        sizeof(struct page *) * num_par_pages);
+
+       size_total = size_ios + size_extra;
+
+       if (likely(size_total <= PAGE_SIZE)) {
+               ios = kzalloc(size_total, GFP_KERNEL);
+               if (unlikely(!ios)) {
+                       ORE_DBGMSG("Failed kzalloc bytes=%zd\n", size_total);
                        *pios = NULL;
                        return -ENOMEM;
                }
-               pages = num_par_pages ? _aios->pages : NULL;
-               sgilist = sgs_per_dev ? _aios->sglist : NULL;
-               ios = &_aios->ios;
+               ios_extra = (char *)ios + size_ios;
        } else {
-               struct __alloc_small_io_state {
-                       struct ore_io_state ios;
-                       struct ore_per_dev_state per_dev[numdevs];
-               } *_aio_small;
-               union __extra_part {
-                       struct osd_sg_entry sglist[sgs_per_dev * numdevs];
-                       struct page *pages[num_par_pages];
-               } *extra_part;
-
-               _aio_small = kzalloc(sizeof(*_aio_small), GFP_KERNEL);
-               if (unlikely(!_aio_small)) {
+               ios = kzalloc(size_ios, GFP_KERNEL);
+               if (unlikely(!ios)) {
                        ORE_DBGMSG("Failed alloc first part bytes=%zd\n",
-                                  sizeof(*_aio_small));
+                                  size_ios);
                        *pios = NULL;
                        return -ENOMEM;
                }
-               extra_part = kzalloc(sizeof(*extra_part), GFP_KERNEL);
-               if (unlikely(!extra_part)) {
+               ios_extra = kzalloc(size_extra, GFP_KERNEL);
+               if (unlikely(!ios_extra)) {
                        ORE_DBGMSG("Failed alloc second part bytes=%zd\n",
-                                  sizeof(*extra_part));
-                       kfree(_aio_small);
+                                  size_extra);
+                       kfree(ios);
                        *pios = NULL;
                        return -ENOMEM;
                }
 
-               pages = num_par_pages ? extra_part->pages : NULL;
-               sgilist = sgs_per_dev ? extra_part->sglist : NULL;
                /* In this case the per_dev[0].sgilist holds the pointer to
                 * be freed
                 */
-               ios = &_aio_small->ios;
                ios->extra_part_alloc = true;
        }
 
-       if (pages) {
-               ios->parity_pages = pages;
+       if (num_par_pages) {
+               ios->parity_pages = ios_extra;
                ios->max_par_pages = num_par_pages;
        }
-       if (sgilist) {
+       if (sgs_per_dev) {
+               struct osd_sg_entry *sgilist = ios_extra;
                unsigned d;
 
                for (d = 0; d < numdevs; ++d) {